1package cmd
  2
  3import (
  4	"fmt"
  5
  6	"github.com/charmbracelet/soft-serve/git"
  7	"github.com/charmbracelet/soft-serve/pkg/backend"
  8	"github.com/charmbracelet/soft-serve/pkg/proto"
  9	"github.com/dustin/go-humanize"
 10	"github.com/spf13/cobra"
 11)
 12
 13// treeCommand returns a command that list file or directory at path.
 14func treeCommand() *cobra.Command {
 15	cmd := &cobra.Command{
 16		Use:               "tree REPOSITORY [REFERENCE] [PATH]",
 17		Short:             "Print repository tree at path",
 18		Args:              cobra.RangeArgs(1, 3),
 19		PersistentPreRunE: checkIfReadable,
 20		RunE: func(cmd *cobra.Command, args []string) error {
 21			ctx := cmd.Context()
 22			be := backend.FromContext(ctx)
 23			rn := args[0]
 24			path := ""
 25			ref := ""
 26			switch len(args) {
 27			case 2:
 28				path = args[1]
 29			case 3:
 30				ref = args[1]
 31				path = args[2]
 32			}
 33			rr, err := be.Repository(ctx, rn)
 34			if err != nil {
 35				return err
 36			}
 37
 38			r, err := rr.Open()
 39			if err != nil {
 40				return err
 41			}
 42
 43			if ref == "" {
 44				head, err := r.HEAD()
 45				if err != nil {
 46					if bs, err := r.Branches(); err != nil && len(bs) == 0 {
 47						return fmt.Errorf("repository is empty")
 48					}
 49					return err
 50				}
 51
 52				ref = head.ID
 53			}
 54
 55			tree, err := r.LsTree(ref)
 56			if err != nil {
 57				return err
 58			}
 59
 60			ents := git.Entries{}
 61			if path != "" && path != "/" {
 62				te, err := tree.TreeEntry(path)
 63				if err == git.ErrRevisionNotExist {
 64					return proto.ErrFileNotFound
 65				}
 66				if err != nil {
 67					return err
 68				}
 69				if te.Type() == "tree" {
 70					tree, err = tree.SubTree(path)
 71					if err != nil {
 72						return err
 73					}
 74					ents, err = tree.Entries()
 75					if err != nil {
 76						return err
 77					}
 78				} else {
 79					ents = append(ents, te)
 80				}
 81			} else {
 82				ents, err = tree.Entries()
 83				if err != nil {
 84					return err
 85				}
 86			}
 87			ents.Sort()
 88			for _, ent := range ents {
 89				size := ent.Size()
 90				ssize := ""
 91				if size == 0 {
 92					ssize = "-"
 93				} else {
 94					ssize = humanize.Bytes(uint64(size))
 95				}
 96				cmd.Printf("%s\t%s\t %s\n", ent.Mode(), ssize, ent.Name())
 97			}
 98			return nil
 99		},
100	}
101	return cmd
102}