1package cmd
 2
 3import (
 4	"fmt"
 5	"path/filepath"
 6	"strings"
 7
 8	"github.com/charmbracelet/soft-serve/git"
 9	"github.com/charmbracelet/soft-serve/server/backend"
10	"github.com/spf13/cobra"
11)
12
13// listCommand returns a command that list file or directory at path.
14func listCommand() *cobra.Command {
15	listCmd := &cobra.Command{
16		Use:     "list PATH",
17		Aliases: []string{"ls"},
18		Short:   "List files at repository.",
19		Args:    cobra.RangeArgs(0, 1),
20		RunE: func(cmd *cobra.Command, args []string) error {
21			cfg, s := fromContext(cmd)
22			rn := ""
23			path := ""
24			ps := []string{}
25			if len(args) > 0 {
26				// FIXME: nested repos are not supported.
27				path = filepath.Clean(args[0])
28				ps = strings.Split(path, "/")
29				rn = strings.TrimSuffix(ps[0], ".git")
30				auth := cfg.Backend.AccessLevel(rn, s.PublicKey())
31				if auth < backend.ReadOnlyAccess {
32					return ErrUnauthorized
33				}
34			}
35			if path == "" || path == "." || path == "/" {
36				repos, err := cfg.Backend.Repositories()
37				if err != nil {
38					return err
39				}
40				for _, r := range repos {
41					if cfg.Backend.AccessLevel(r.Name(), s.PublicKey()) >= backend.ReadOnlyAccess {
42						cmd.Println(r.Name())
43					}
44				}
45				return nil
46			}
47			rr, err := cfg.Backend.Repository(rn)
48			if err != nil {
49				return err
50			}
51			r, err := rr.Open()
52			if err != nil {
53				return err
54			}
55			head, err := r.HEAD()
56			if err != nil {
57				if bs, err := r.Branches(); err != nil && len(bs) == 0 {
58					return fmt.Errorf("repository is empty")
59				}
60				return err
61			}
62			tree, err := r.TreePath(head, "")
63			if err != nil {
64				return err
65			}
66			subpath := strings.Join(ps[1:], "/")
67			ents := git.Entries{}
68			te, err := tree.TreeEntry(subpath)
69			if err == git.ErrRevisionNotExist {
70				return ErrFileNotFound
71			}
72			if err != nil {
73				return err
74			}
75			if te.Type() == "tree" {
76				tree, err = tree.SubTree(subpath)
77				if err != nil {
78					return err
79				}
80				ents, err = tree.Entries()
81				if err != nil {
82					return err
83				}
84			} else {
85				ents = append(ents, te)
86			}
87			ents.Sort()
88			for _, ent := range ents {
89				cmd.Printf("%s\t%d\t %s\n", ent.Mode(), ent.Size(), ent.Name())
90			}
91			return nil
92		},
93	}
94	return listCmd
95}