diff --git a/cmd/stbak/cmd/inventory_list.go b/cmd/stbak/cmd/inventory_list.go index 58cfc2f..c499248 100644 --- a/cmd/stbak/cmd/inventory_list.go +++ b/cmd/stbak/cmd/inventory_list.go @@ -9,6 +9,10 @@ import ( "github.com/spf13/viper" ) +const ( + limitFlag = "limit" +) + var inventoryListCmd = &cobra.Command{ Use: "list", Aliases: []string{"lis", "l", "t", "ls"}, @@ -29,6 +33,7 @@ var inventoryListCmd = &cobra.Command{ }, viper.GetString(nameFlag), + viper.GetInt(limitFlag), logging.NewLogger().PrintHeader, ); err != nil { @@ -41,6 +46,7 @@ var inventoryListCmd = &cobra.Command{ func init() { inventoryListCmd.PersistentFlags().StringP(nameFlag, "n", "", "Directory to list the contents of") + inventoryListCmd.PersistentFlags().IntP(limitFlag, "l", -1, "Maximum amount of files to list (-1 lists all)") viper.AutomaticEnv() diff --git a/internal/fs/file.go b/internal/fs/file.go index d03cdaa..f87b6db 100644 --- a/internal/fs/file.go +++ b/internal/fs/file.go @@ -80,6 +80,7 @@ func (f *File) Readdir(count int) ([]os.FileInfo, error) { f.metadata, f.path, + count, f.onHeader, ) @@ -89,7 +90,6 @@ func (f *File) Readdir(count int) ([]os.FileInfo, error) { fileInfos := []os.FileInfo{} for _, hdr := range hdrs { - // TODO: Handle count; only return all if count = -1 fileInfos = append(fileInfos, NewFileInfo(hdr)) } diff --git a/internal/persisters/metadata.go b/internal/persisters/metadata.go index dbb5b52..6ee97eb 100644 --- a/internal/persisters/metadata.go +++ b/internal/persisters/metadata.go @@ -187,7 +187,7 @@ func (p *MetadataPersister) GetHeaderChildren(ctx context.Context, name string) return outhdrs, nil } -func (p *MetadataPersister) GetHeaderDirectChildren(ctx context.Context, name string) (models.HeaderSlice, error) { +func (p *MetadataPersister) GetHeaderDirectChildren(ctx context.Context, name string, limit int) (models.HeaderSlice, error) { prefix := strings.TrimSuffix(name, "/") + "/" rootDepth := 0 headers := models.HeaderSlice{} @@ -217,9 +217,8 @@ func (p *MetadataPersister) GetHeaderDirectChildren(ctx context.Context, name st } getHeaders := func(prefix string) (models.HeaderSlice, error) { - if err := queries.Raw( - fmt.Sprintf( - `select %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, + query := fmt.Sprintf( + `select %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v, length(replace(%v, ?, '')) - length(replace(replace(%v, ?, ''), '/', '')) as depth from %v where %v like ? @@ -232,35 +231,56 @@ where %v like ? ) and %v != 1 and not %v in ('', '.', '/', './')`, - models.HeaderColumns.Record, - models.HeaderColumns.Lastknownrecord, - models.HeaderColumns.Block, - models.HeaderColumns.Lastknownblock, - models.HeaderColumns.Deleted, - models.HeaderColumns.Typeflag, - models.HeaderColumns.Name, - models.HeaderColumns.Linkname, - models.HeaderColumns.Size, - models.HeaderColumns.Mode, - models.HeaderColumns.UID, - models.HeaderColumns.Gid, - models.HeaderColumns.Uname, - models.HeaderColumns.Gname, - models.HeaderColumns.Modtime, - models.HeaderColumns.Accesstime, - models.HeaderColumns.Changetime, - models.HeaderColumns.Devmajor, - models.HeaderColumns.Devminor, - models.HeaderColumns.Paxrecords, - models.HeaderColumns.Format, - models.HeaderColumns.Name, - models.HeaderColumns.Name, - models.TableNames.Headers, - models.HeaderColumns.Name, - models.HeaderColumns.Name, - models.HeaderColumns.Deleted, - models.HeaderColumns.Name, - ), + models.HeaderColumns.Record, + models.HeaderColumns.Lastknownrecord, + models.HeaderColumns.Block, + models.HeaderColumns.Lastknownblock, + models.HeaderColumns.Deleted, + models.HeaderColumns.Typeflag, + models.HeaderColumns.Name, + models.HeaderColumns.Linkname, + models.HeaderColumns.Size, + models.HeaderColumns.Mode, + models.HeaderColumns.UID, + models.HeaderColumns.Gid, + models.HeaderColumns.Uname, + models.HeaderColumns.Gname, + models.HeaderColumns.Modtime, + models.HeaderColumns.Accesstime, + models.HeaderColumns.Changetime, + models.HeaderColumns.Devmajor, + models.HeaderColumns.Devminor, + models.HeaderColumns.Paxrecords, + models.HeaderColumns.Format, + models.HeaderColumns.Name, + models.HeaderColumns.Name, + models.TableNames.Headers, + models.HeaderColumns.Name, + models.HeaderColumns.Name, + models.HeaderColumns.Deleted, + models.HeaderColumns.Name, + ) + + if limit < 0 { + if err := queries.Raw( + query+`limit ?`, + prefix, + prefix, + prefix+"%", + rootDepth, + rootDepth+1, + limit+1, // +1 to accomodate the parent directory if it exists + ).Bind(ctx, p.db, &headers); err != nil { + if err == sql.ErrNoRows { + return headers, nil + } + + return nil, err + } + } + + if err := queries.Raw( + query, prefix, prefix, prefix+"%", @@ -297,7 +317,11 @@ where %v like ? } } - return outhdrs, nil + if limit < 0 || len(outhdrs) < limit { + return outhdrs, nil + } + + return outhdrs[:limit-1], nil } func (p *MetadataPersister) DeleteHeader(ctx context.Context, name string, lastknownrecord, lastknownblock int64) (*models.Header, error) { diff --git a/pkg/inventory/list.go b/pkg/inventory/list.go index 627f9ef..5f6b279 100644 --- a/pkg/inventory/list.go +++ b/pkg/inventory/list.go @@ -13,10 +13,11 @@ func List( metadata config.MetadataConfig, name string, + limit int, onHeader func(hdr *models.Header), ) ([]*tar.Header, error) { - dbHdrs, err := metadata.Metadata.GetHeaderDirectChildren(context.Background(), name) + dbHdrs, err := metadata.Metadata.GetHeaderDirectChildren(context.Background(), name, limit) if err != nil { return []*tar.Header{}, err }