aboutsummaryrefslogtreecommitdiffstats
path: root/formats/packfile/packfile.go
blob: d70f396e5b67501278fb47615242d4b8305a47e2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package packfile

import "fmt"

type Packfile struct {
	Version     uint32
	Size        int64
	ObjectCount int
	Checksum    []byte
	Commits     map[Hash]*Commit
	Trees       map[Hash]*Tree
	Blobs       map[Hash]*Blob
}

func NewPackfile() *Packfile {
	return &Packfile{
		Commits: make(map[Hash]*Commit, 0),
		Trees:   make(map[Hash]*Tree, 0),
		Blobs:   make(map[Hash]*Blob, 0),
	}
}

type BlobEntry struct {
	path string
	*Blob
}

type SubtreeEntry struct {
	path string
	*Tree
	TreeCh
}

type treeEntry interface {
	isTreeEntry()
	Path() string
}

func (b BlobEntry) isTreeEntry()    {}
func (b BlobEntry) Path() string    { return b.path }
func (b SubtreeEntry) isTreeEntry() {}
func (b SubtreeEntry) Path() string { return b.path }

type TreeCh <-chan treeEntry

func (p *Packfile) WalkCommit(commitHash Hash) (TreeCh, error) {
	commit, ok := p.Commits[commitHash]
	if !ok {
		return nil, fmt.Errorf("Unable to find %q commit", commitHash)
	}

	return p.WalkTree(p.Trees[commit.Tree]), nil
}

func (p *Packfile) WalkTree(tree *Tree) TreeCh {
	return p.walkTree(tree, "")
}

func (p *Packfile) walkTree(tree *Tree, pathPrefix string) TreeCh {
	ch := make(chan treeEntry)

	if tree == nil {
		close(ch)
		return ch
	}

	go func() {
		defer func() {
			close(ch)
		}()
		for _, e := range tree.Entries {
			path := pathPrefix + e.Name
			if blob, ok := p.Blobs[e.Hash]; ok {
				ch <- BlobEntry{path, blob}
			} else if subtree, ok := p.Trees[e.Hash]; ok {
				ch <- SubtreeEntry{path, subtree, p.walkTree(subtree, path+"/")}
			}
		}
	}()

	return ch
}