github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/cmd/list.go (about) 1 package cmd 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "errors" 8 "fmt" 9 10 "github.com/qri-io/dataset" 11 "github.com/qri-io/ioes" 12 "github.com/qri-io/qri/base/params" 13 "github.com/qri-io/qri/dsref" 14 "github.com/qri-io/qri/lib" 15 "github.com/spf13/cobra" 16 ) 17 18 // NewListCommand creates new `qri list` command that lists datasets for the local peer & others 19 func NewListCommand(f Factory, ioStreams ioes.IOStreams) *cobra.Command { 20 o := &ListOptions{IOStreams: ioStreams} 21 cmd := &cobra.Command{ 22 Use: "list [FILTER]", 23 Aliases: []string{"ls"}, 24 Short: "show a list of datasets", 25 Long: `List shows lists of datasets, including names and current hashes. 26 27 The default list is the latest version of all datasets you have on your local 28 qri repository. The first argument can be used to find datasets with a certain 29 substring in their name. 30 31 When used in conjunction with ` + "`qri connect`" + `, list can list a peer's dataset. You 32 must have ` + "`qri connect`" + ` running in a separate terminal window.`, 33 Example: ` # Show all of your datasets: 34 $ qri list 35 36 # Show datasets with the substring "new" in their name: 37 $ qri list new 38 39 # To view the list of a peer's datasets... 40 # In one terminal window: 41 $ qri connect 42 # In a separate terminal window, show all of b5's datasets: 43 $ qri list --peer b5`, 44 Annotations: map[string]string{ 45 "group": "dataset", 46 }, 47 Args: cobra.MaximumNArgs(1), 48 RunE: func(cmd *cobra.Command, args []string) error { 49 if err := o.Complete(f, args); err != nil { 50 return err 51 } 52 return o.Run() 53 }, 54 } 55 56 cmd.Flags().StringVarP(&o.Format, "format", "f", "", "set output format [json|simple]") 57 cmd.Flags().IntVar(&o.Offset, "offset", 0, "skip this number of records from the results, default 0") 58 cmd.Flags().IntVar(&o.Limit, "limit", 25, "size of results, default 25") 59 cmd.Flags().BoolVar(&o.All, "all", false, "get all results") 60 cmd.Flags().BoolVarP(&o.Public, "public", "p", false, "list only publically visible") 61 cmd.Flags().BoolVarP(&o.ShowNumVersions, "num-versions", "n", false, "show number of versions") 62 cmd.Flags().StringVar(&o.Username, "user", "", "user whose datasets to list") 63 cmd.MarkFlagCustom("user", "__qri_get_user_flag_suggestions") 64 cmd.Flags().BoolVarP(&o.Raw, "raw", "r", false, "to show raw references") 65 66 return cmd 67 } 68 69 // ListOptions encapsulates state for the List command 70 type ListOptions struct { 71 ioes.IOStreams 72 73 Format string 74 Offset int 75 Limit int 76 All bool 77 Term string 78 Username string 79 Public bool 80 ShowNumVersions bool 81 Raw bool 82 83 inst *lib.Instance 84 } 85 86 // Complete adds any missing configuration that can only be added just before calling Run 87 func (o *ListOptions) Complete(f Factory, args []string) (err error) { 88 if len(args) > 0 { 89 o.Term = args[0] 90 } 91 o.inst, err = f.Instance() 92 return 93 } 94 95 // Run executes the list command 96 func (o *ListOptions) Run() (err error) { 97 ctx := context.TODO() 98 99 if o.Raw { 100 text, err := o.inst.Collection().ListRawRefs(ctx, &lib.EmptyParams{}) 101 if err != nil { 102 return err 103 } 104 printSuccess(o.Out, text) 105 return nil 106 } 107 108 p := &lib.CollectionListParams{ 109 Term: o.Term, 110 Username: o.Username, 111 List: params.List{ 112 Offset: o.Offset, 113 Limit: o.Limit, 114 }, 115 Public: o.Public, 116 } 117 infos, cur, err := o.inst.Collection().List(ctx, p) 118 if err != nil { 119 if errors.Is(err, lib.ErrListWarning) { 120 printWarning(o.ErrOut, fmt.Sprintf("%s\n", err)) 121 err = nil 122 } else { 123 return err 124 } 125 } 126 127 // TODO(dustmop): Generics (Go1.17?) will make this refactorable 128 // Consume the entire Cursor to list all references 129 if cur != nil && o.All { 130 isDone := false 131 for { 132 more, err := cur.Next(ctx) 133 if err == lib.ErrCursorComplete { 134 // Don't break just yet, `more` may have remaining items to append 135 isDone = true 136 } else if err != nil { 137 return err 138 } 139 if vals, ok := more.([]dsref.VersionInfo); ok { 140 if len(vals) == 0 { 141 isDone = true 142 } 143 infos = append(infos, vals...) 144 } 145 if isDone { 146 break 147 } 148 } 149 } 150 151 for _, ref := range infos { 152 // remove profileID so names print pretty 153 ref.ProfileID = "" 154 } 155 156 if len(infos) == 0 { 157 pn := fmt.Sprintf("%s has", o.Username) 158 if o.Username == "" { 159 pn = "you have" 160 } 161 162 if o.Term == "" { 163 printInfo(o.Out, "%s no datasets", pn) 164 } else { 165 printInfo(o.Out, "%s no datasets that match \"%s\"", pn, o.Term) 166 } 167 return 168 } 169 170 switch o.Format { 171 case "": 172 items := make([]fmt.Stringer, len(infos)) 173 for i, r := range infos { 174 items[i] = versionInfoStringer(r) 175 } 176 printItems(o.Out, items, o.Offset) 177 return nil 178 case "simple": 179 items := make([]string, len(infos)) 180 for i, r := range infos { 181 items[i] = r.SimpleRef().Alias() 182 } 183 printlnStringItems(o.Out, items) 184 return nil 185 case dataset.JSONDataFormat.String(): 186 data, err := json.MarshalIndent(infos, "", " ") 187 if err != nil { 188 return err 189 } 190 buf := bytes.NewBuffer(data) 191 printToPager(o.Out, buf) 192 return nil 193 default: 194 return fmt.Errorf("unrecognized format: %s", o.Format) 195 } 196 }