forked from cerc-io/ipld-eth-server
171 lines
2.8 KiB
Go
171 lines
2.8 KiB
Go
package ipldgit
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"sync"
|
|
|
|
cid "github.com/ipfs/go-cid"
|
|
node "github.com/ipfs/go-ipld-format"
|
|
)
|
|
|
|
type Tree struct {
|
|
entries map[string]*TreeEntry
|
|
size int
|
|
order []string
|
|
cid cid.Cid
|
|
rawData []byte
|
|
rawDataOnce sync.Once
|
|
}
|
|
|
|
type TreeEntry struct {
|
|
name string
|
|
Mode string `json:"mode"`
|
|
Hash cid.Cid `json:"hash"`
|
|
}
|
|
|
|
func (t *Tree) Cid() cid.Cid {
|
|
return t.cid
|
|
}
|
|
|
|
func (t *Tree) String() string {
|
|
return "[git tree object]"
|
|
}
|
|
|
|
func (t *Tree) GitSha() []byte {
|
|
return cidToSha(t.cid)
|
|
}
|
|
|
|
func (t *Tree) Copy() node.Node {
|
|
out := &Tree{
|
|
entries: make(map[string]*TreeEntry),
|
|
cid: t.cid,
|
|
size: t.size,
|
|
order: t.order, // TODO: make a deep copy of this
|
|
}
|
|
|
|
for k, v := range t.entries {
|
|
nv := *v
|
|
out.entries[k] = &nv
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (t *Tree) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(t.entries)
|
|
}
|
|
|
|
func (t *Tree) Tree(p string, depth int) []string {
|
|
if p != "" {
|
|
_, ok := t.entries[p]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
return []string{"mode", "type", "hash"}
|
|
}
|
|
|
|
if depth == 0 {
|
|
return nil
|
|
}
|
|
|
|
if depth == 1 {
|
|
return t.order
|
|
}
|
|
|
|
var out []string
|
|
for k, _ := range t.entries {
|
|
out = append(out, k, k+"/mode", k+"/type", k+"/hash")
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (t *Tree) Links() []*node.Link {
|
|
var out []*node.Link
|
|
for _, v := range t.entries {
|
|
out = append(out, &node.Link{Cid: v.Hash})
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (t *Tree) Loggable() map[string]interface{} {
|
|
return map[string]interface{}{
|
|
"type": "git tree object",
|
|
}
|
|
}
|
|
|
|
func (t *Tree) RawData() []byte {
|
|
t.rawDataOnce.Do(func() {
|
|
buf := new(bytes.Buffer)
|
|
|
|
fmt.Fprintf(buf, "tree %d\x00", t.size)
|
|
for _, s := range t.order {
|
|
t.entries[s].WriteTo(buf)
|
|
}
|
|
t.rawData = buf.Bytes()
|
|
})
|
|
|
|
return t.rawData
|
|
}
|
|
|
|
func (t *Tree) Resolve(p []string) (interface{}, []string, error) {
|
|
e, ok := t.entries[p[0]]
|
|
if !ok {
|
|
return nil, nil, errors.New("no such link")
|
|
}
|
|
|
|
if len(p) == 1 {
|
|
return e, nil, nil
|
|
}
|
|
|
|
switch p[1] {
|
|
case "hash":
|
|
return &node.Link{Cid: e.Hash}, p[2:], nil
|
|
case "mode":
|
|
return e.Mode, p[2:], nil
|
|
default:
|
|
return nil, nil, errors.New("no such link")
|
|
}
|
|
}
|
|
|
|
func (t Tree) ResolveLink(path []string) (*node.Link, []string, error) {
|
|
out, rest, err := t.Resolve(path)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
lnk, ok := out.(*node.Link)
|
|
if !ok {
|
|
return nil, nil, errors.New("not a link")
|
|
}
|
|
|
|
return lnk, rest, nil
|
|
}
|
|
|
|
func (t *Tree) Size() (uint64, error) {
|
|
return uint64(len(t.RawData())), nil
|
|
}
|
|
|
|
func (t *Tree) Stat() (*node.NodeStat, error) {
|
|
return &node.NodeStat{}, nil
|
|
}
|
|
|
|
func (te *TreeEntry) WriteTo(w io.Writer) (int, error) {
|
|
n, err := fmt.Fprintf(w, "%s %s\x00", te.Mode, te.name)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
nn, err := w.Write(cidToSha(te.Hash))
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
|
|
return n + nn, nil
|
|
}
|
|
|
|
var _ node.Node = (*Tree)(nil)
|