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