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 }