wrds-download

TUI/CLI tool for browsing and downloading WRDS data
Log | Files | Refs | README

info.go (3251B)


      1 package cmd
      2 
      3 import (
      4 	"context"
      5 	"encoding/json"
      6 	"fmt"
      7 	"os"
      8 	"text/tabwriter"
      9 	"time"
     10 
     11 	"github.com/louloulibs/wrds-download/internal/config"
     12 	"github.com/louloulibs/wrds-download/internal/db"
     13 	"github.com/spf13/cobra"
     14 )
     15 
     16 var (
     17 	infoSchema string
     18 	infoTable  string
     19 	infoJSON   bool
     20 )
     21 
     22 var infoCmd = &cobra.Command{
     23 	Use:   "info",
     24 	Short: "Show table metadata (columns, types, row count)",
     25 	Long: `Display metadata for a WRDS table: comment, estimated row count,
     26 size, and column details (name, type, nullable, description).
     27 
     28 Examples:
     29   wrds-dl info --schema crsp --table dsf
     30   wrds-dl info --schema comp --table funda --json`,
     31 	RunE: runInfo,
     32 }
     33 
     34 func init() {
     35 	rootCmd.AddCommand(infoCmd)
     36 
     37 	f := infoCmd.Flags()
     38 	f.StringVar(&infoSchema, "schema", "", "Schema name (required)")
     39 	f.StringVar(&infoTable, "table", "", "Table name (required)")
     40 	f.BoolVar(&infoJSON, "json", false, "Output as JSON")
     41 
     42 	_ = infoCmd.MarkFlagRequired("schema")
     43 	_ = infoCmd.MarkFlagRequired("table")
     44 }
     45 
     46 func runInfo(cmd *cobra.Command, args []string) error {
     47 	config.ApplyCredentials()
     48 
     49 	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
     50 	defer cancel()
     51 
     52 	client, err := db.New(ctx)
     53 	if err != nil {
     54 		return fmt.Errorf("connect: %w", err)
     55 	}
     56 	defer client.Close()
     57 
     58 	meta, err := client.TableMeta(ctx, infoSchema, infoTable)
     59 	if err != nil {
     60 		return fmt.Errorf("table meta: %w", err)
     61 	}
     62 
     63 	if infoJSON {
     64 		return printInfoJSON(meta)
     65 	}
     66 	return printInfoTable(meta)
     67 }
     68 
     69 type jsonColumn struct {
     70 	Name        string `json:"name"`
     71 	Type        string `json:"type"`
     72 	Nullable    bool   `json:"nullable"`
     73 	Description string `json:"description,omitempty"`
     74 }
     75 
     76 type jsonInfo struct {
     77 	Schema   string       `json:"schema"`
     78 	Table    string       `json:"table"`
     79 	Comment  string       `json:"comment,omitempty"`
     80 	RowCount int64        `json:"row_count"`
     81 	Size     string       `json:"size,omitempty"`
     82 	Columns  []jsonColumn `json:"columns"`
     83 }
     84 
     85 func printInfoJSON(meta *db.TableMeta) error {
     86 	info := jsonInfo{
     87 		Schema:   meta.Schema,
     88 		Table:    meta.Table,
     89 		Comment:  meta.Comment,
     90 		RowCount: meta.RowCount,
     91 		Size:     meta.Size,
     92 		Columns:  make([]jsonColumn, len(meta.Columns)),
     93 	}
     94 	for i, c := range meta.Columns {
     95 		info.Columns[i] = jsonColumn{
     96 			Name:        c.Name,
     97 			Type:        c.DataType,
     98 			Nullable:    c.Nullable,
     99 			Description: c.Description,
    100 		}
    101 	}
    102 
    103 	enc := json.NewEncoder(os.Stdout)
    104 	enc.SetIndent("", "  ")
    105 	return enc.Encode(info)
    106 }
    107 
    108 func printInfoTable(meta *db.TableMeta) error {
    109 	fmt.Fprintf(os.Stdout, "%s.%s\n", meta.Schema, meta.Table)
    110 	if meta.Comment != "" {
    111 		fmt.Fprintf(os.Stdout, "  %s\n", meta.Comment)
    112 	}
    113 	if meta.RowCount > 0 || meta.Size != "" {
    114 		parts := ""
    115 		if meta.RowCount > 0 {
    116 			parts += fmt.Sprintf("~%d rows", meta.RowCount)
    117 		}
    118 		if meta.Size != "" {
    119 			if parts != "" {
    120 				parts += ", "
    121 			}
    122 			parts += meta.Size
    123 		}
    124 		fmt.Fprintf(os.Stdout, "  %s\n", parts)
    125 	}
    126 	fmt.Fprintln(os.Stdout)
    127 
    128 	w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
    129 	fmt.Fprintln(w, "NAME\tTYPE\tNULLABLE\tDESCRIPTION")
    130 	for _, c := range meta.Columns {
    131 		nullable := "NO"
    132 		if c.Nullable {
    133 			nullable = "YES"
    134 		}
    135 		fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", c.Name, c.DataType, nullable, c.Description)
    136 	}
    137 	return w.Flush()
    138 }