diff --git a/internal/store/root/list.go b/internal/store/root/list.go index 6801faf262..856cd0fcaf 100644 --- a/internal/store/root/list.go +++ b/internal/store/root/list.go @@ -9,6 +9,7 @@ import ( "github.com/gopasspw/gopass/internal/out" "github.com/gopasspw/gopass/internal/store" "github.com/gopasspw/gopass/internal/tree" + "github.com/gopasspw/gopass/pkg/debug" ) // List will return a flattened list of all tree entries. @@ -61,7 +62,9 @@ func (r *Store) Tree(ctx context.Context) (*tree.Root, error) { return nil, err } + debug.Log("[root] adding files: %q", sf) addFileFunc(sf...) + debug.Log("[root] Tree: %s", root.Format(-1)) addTplFunc(r.store.ListTemplates(ctx, "")...) mps := r.MountPoints() @@ -82,6 +85,7 @@ func (r *Store) Tree(ctx context.Context) (*tree.Root, error) { return nil, fmt.Errorf("failed to add file: %w", err) } + debug.Log("[%s] adding files: %q", alias, sf) addFileFunc(sf...) addTplFunc(substore.ListTemplates(ctx, alias)...) } diff --git a/internal/tree/node.go b/internal/tree/node.go index 2744fe4ee9..57bde74b6d 100644 --- a/internal/tree/node.go +++ b/internal/tree/node.go @@ -2,12 +2,14 @@ package tree import ( "bytes" + + "github.com/gopasspw/gopass/pkg/debug" ) // Node is a tree node. type Node struct { Name string - Type string + Leaf bool Template bool Mount bool Path string @@ -40,7 +42,7 @@ func (n Node) Equals(other Node) bool { return false } - if n.Type != other.Type { + if n.Leaf != other.Leaf { return false } @@ -59,6 +61,35 @@ func (n Node) Equals(other Node) bool { return true } +func (n Node) Merge(other Node) *Node { + r := Node{ + Name: n.Name, + Leaf: n.Leaf, + Template: n.Template, + Mount: n.Mount, + Path: n.Path, + Subtree: n.Subtree, + } + + // can't change name + if other.Leaf { + r.Leaf = true + } + if other.Template { + r.Template = true + } + if other.Mount { + r.Mount = true + } + // can't change path + if r.Subtree == nil && other.Subtree != nil { + r.Subtree = other.Subtree + } + debug.Log("merged %+v and %+v into %+v", n, other, r) + + return &r +} + // format returns a pretty printed string of all nodes in and below // this node, e.g. `├── baz`. func (n *Node) format(prefix string, last bool, maxDepth, curDepth int) string { @@ -86,7 +117,7 @@ func (n *Node) format(prefix string, last bool, maxDepth, curDepth int) string { switch { case n.Mount: _, _ = out.WriteString(colMount(n.Name + " (" + n.Path + ")")) - case n.Type == "dir": + case n.Subtree != nil: _, _ = out.WriteString(colDir(n.Name + sep)) default: _, _ = out.WriteString(n.Name) @@ -113,7 +144,7 @@ func (n *Node) format(prefix string, last bool, maxDepth, curDepth int) string { // Len returns the length of this subtree. func (n *Node) Len() int { - if n.Type == "file" { + if n.Subtree == nil { return 1 } @@ -137,17 +168,17 @@ func (n *Node) list(prefix string, maxDepth, curDepth int, files bool) []string prefix += n.Name + out := make([]string, 0, n.Len()) // if it's a file and we are looking for files - if n.Type == "file" && files { + if n.Leaf && files { // we return the file - return []string{prefix} - } else if curDepth == maxDepth && n.Type != "file" { + out = append(out, prefix) + } else if curDepth == maxDepth && n.Subtree != nil { // otherwise if we are "at the bottom" and it's not a file // we return the directory name with a separator at the end return []string{prefix + sep} } - out := make([]string, 0, n.Len()) // if we don't have subitems, then it's a leaf and we return // (notice that this is what ends the recursion when maxDepth is set to -1) if n.Subtree == nil { diff --git a/internal/tree/root.go b/internal/tree/root.go index 47b3f6c8b8..cbbef359d1 100644 --- a/internal/tree/root.go +++ b/internal/tree/root.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/fatih/color" + "github.com/gopasspw/gopass/pkg/debug" ) const ( @@ -59,18 +60,18 @@ func (r *Root) AddTemplate(path string) error { func (r *Root) insert(path string, template bool, mountPath string) error { t := r.Subtree + debug.Log("adding: %s [tpl: %t, mp: %q]", path, template, mountPath) // split the path into its components, iterate over them and create // the tree structure. Everything but the last element is a folder. p := strings.Split(path, "/") for i, e := range p { n := &Node{ Name: e, - Type: "dir", Subtree: NewTree(), } // this is the final element (a leaf) if i == len(p)-1 { - n.Type = "file" + n.Leaf = true n.Subtree = nil n.Template = template @@ -80,11 +81,15 @@ func (r *Root) insert(path string, template bool, mountPath string) error { } } - node, _ := t.Insert(n) + debug.Log("[%d] %s -> Node: %+v", i, e, n) + + node := t.Insert(n) + debug.Log("node after insert: %+v", node) + // do we need to extend an existing subtree? if i < len(p)-1 && node.Subtree == nil { node.Subtree = NewTree() - node.Type = "dir" + // node.Type = "dir" } // re-root t to the new subtree @@ -147,8 +152,8 @@ func (r *Root) FindFolder(path string) (*Root, error) { prefix := "" for _, e := range p { - _, node := t.find(e) - if node == nil || node.Type == "file" || node.Subtree == nil { + _, node := t.findPositionFor(e) + if node == nil || node.Subtree == nil { return nil, ErrNotFound } diff --git a/internal/tree/tree.go b/internal/tree/tree.go index 86005b63d4..affd8509b5 100644 --- a/internal/tree/tree.go +++ b/internal/tree/tree.go @@ -6,6 +6,8 @@ package tree import ( "fmt" "sort" + + "github.com/gopasspw/gopass/pkg/debug" ) // ErrNodePresent is returned when a node with the same name is already present. @@ -44,28 +46,27 @@ func (t *Tree) Equals(other *Tree) bool { } // Insert adds a new node at the right position. -func (t *Tree) Insert(node *Node) (*Node, error) { - pos, found := t.find(node.Name) +func (t *Tree) Insert(node *Node) *Node { + pos, found := t.findPositionFor(node.Name) if found != nil { - if node.Mount { - t.Nodes[pos] = node - - return node, nil - } + debug.Log("merging node (%+v) with existing one (%+v)", found, node) + m := found.Merge(*node) + t.Nodes[pos] = m - return t.Nodes[pos], fmt.Errorf("error at %q: %w", node.Name, ErrNodePresent) + return m } + debug.Log("extending subtree for %s", node.Name) // insert at the right position, see // https://code.google.com/p/go-wiki/wiki/SliceTricks t.Nodes = append(t.Nodes, &Node{}) copy(t.Nodes[pos+1:], t.Nodes[pos:]) t.Nodes[pos] = node - return node, nil + return node } -func (t *Tree) find(name string) (int, *Node) { +func (t *Tree) findPositionFor(name string) (int, *Node) { pos := sort.Search(len(t.Nodes), func(i int) bool { return t.Nodes[i].Name >= name })