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  }