tree_entry.go

  1package repository
  2
  3import (
  4	"bytes"
  5	"fmt"
  6	"io"
  7	"strconv"
  8	"strings"
  9)
 10
 11type TreeEntry struct {
 12	ObjectType ObjectType
 13	Hash       Hash
 14	Name       string
 15}
 16
 17type ObjectType int
 18
 19const (
 20	Unknown    ObjectType = iota
 21	Blob                  // regular file      (100644)
 22	Tree                  // directory         (040000)
 23	Executable            // executable file   (100755)
 24	Symlink               // symbolic link     (120000)
 25	Submodule             // git submodule     (160000)
 26)
 27
 28func ParseTreeEntry(line string) (TreeEntry, error) {
 29	fields := strings.Fields(line)
 30
 31	if len(fields) < 4 {
 32		return TreeEntry{}, fmt.Errorf("Invalid input to parse as a TreeEntry")
 33	}
 34
 35	objType, err := ParseObjectType(fields[0], fields[1])
 36
 37	if err != nil {
 38		return TreeEntry{}, err
 39	}
 40
 41	hash := Hash(fields[2])
 42	name := strings.Join(fields[3:], "")
 43
 44	return TreeEntry{
 45		ObjectType: objType,
 46		Hash:       hash,
 47		Name:       name,
 48	}, nil
 49}
 50
 51// Format the entry as a git ls-tree compatible line
 52func (entry TreeEntry) Format() string {
 53	return fmt.Sprintf("%s %s\t%s\n", entry.ObjectType.Format(), entry.Hash, entry.Name)
 54}
 55
 56func (ot ObjectType) Format() string {
 57	switch ot {
 58	case Blob:
 59		return "100644 blob"
 60	case Tree:
 61		return "040000 tree"
 62	case Executable:
 63		return "100755 blob"
 64	case Symlink:
 65		return "120000 blob"
 66	case Submodule:
 67		return "160000 commit"
 68	default:
 69		panic("Unknown git object type")
 70	}
 71}
 72
 73func (ot ObjectType) MarshalGQL(w io.Writer) {
 74	switch ot {
 75	case Tree:
 76		fmt.Fprint(w, strconv.Quote("TREE"))
 77	case Blob, Executable:
 78		fmt.Fprint(w, strconv.Quote("BLOB"))
 79	case Symlink:
 80		fmt.Fprint(w, strconv.Quote("SYMLINK"))
 81	case Submodule:
 82		fmt.Fprint(w, strconv.Quote("SUBMODULE"))
 83	default:
 84		panic(fmt.Sprintf("unknown ObjectType value %d", int(ot)))
 85	}
 86}
 87
 88func (ot *ObjectType) UnmarshalGQL(v any) error {
 89	str, ok := v.(string)
 90	if !ok {
 91		return fmt.Errorf("enums must be strings")
 92	}
 93	switch str {
 94	case "TREE":
 95		*ot = Tree
 96	case "BLOB":
 97		*ot = Blob
 98	case "SYMLINK":
 99		*ot = Symlink
100	case "SUBMODULE":
101		*ot = Submodule
102	default:
103		return fmt.Errorf("%q is not a valid ObjectType", str)
104	}
105	return nil
106}
107
108func ParseObjectType(mode, objType string) (ObjectType, error) {
109	switch {
110	case mode == "100644" && objType == "blob":
111		return Blob, nil
112	case mode == "040000" && objType == "tree":
113		return Tree, nil
114	case mode == "100755" && objType == "blob":
115		return Executable, nil
116	case mode == "120000" && objType == "blob":
117		return Symlink, nil
118	case mode == "160000" && objType == "commit":
119		return Submodule, nil
120	default:
121		return Unknown, fmt.Errorf("Unknown git object type %s %s", mode, objType)
122	}
123}
124
125func prepareTreeEntries(entries []TreeEntry) bytes.Buffer {
126	var buffer bytes.Buffer
127
128	for _, entry := range entries {
129		buffer.WriteString(entry.Format())
130	}
131
132	return buffer
133}
134
135func readTreeEntries(s string) ([]TreeEntry, error) {
136	split := strings.Split(strings.TrimSpace(s), "\n")
137
138	casted := make([]TreeEntry, len(split))
139	for i, line := range split {
140		if line == "" {
141			continue
142		}
143
144		entry, err := ParseTreeEntry(line)
145
146		if err != nil {
147			return nil, err
148		}
149
150		casted[i] = entry
151	}
152
153	return casted, nil
154}
155
156// SearchTreeEntry search a TreeEntry by name from an array
157func SearchTreeEntry(entries []TreeEntry, name string) (TreeEntry, bool) {
158	for _, entry := range entries {
159		if entry.Name == name {
160			return entry, true
161		}
162	}
163	return TreeEntry{}, false
164}