mirror of
https://github.com/gopasspw/gopass.git
synced 2025-12-08 19:24:54 +00:00
* [feat] Add verbosity levels to the debug package Use debug.V(N).Log instead of debug.Log to indicate message verbosity (higher numbers indicate more verbose messages). Use GOPASS_DEBUG_VERBOSE=N to control the desired level of verbosity in the log output. Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> * Document the verbosity env vars. Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> * Allow negative verbosity values Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> --------- Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org>
183 lines
4.1 KiB
Go
183 lines
4.1 KiB
Go
package tree
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/fatih/color"
|
|
"github.com/gopasspw/gopass/pkg/debug"
|
|
)
|
|
|
|
const (
|
|
symEmpty = " "
|
|
symBranch = "├── "
|
|
symLeaf = "└── "
|
|
symVert = "│ "
|
|
)
|
|
|
|
var (
|
|
// ErrNotFound is returned when a node is not found.
|
|
ErrNotFound = fmt.Errorf("not found")
|
|
colMount = color.New(color.FgCyan, color.Bold).SprintfFunc()
|
|
colDir = color.New(color.FgBlue, color.Bold).SprintfFunc()
|
|
colTpl = color.New(color.FgGreen, color.Bold).SprintfFunc()
|
|
colShadow = color.New(color.FgRed, color.Bold).SprintfFunc()
|
|
// sep is intentionally NOT platform-agnostic. This is used for the CLI output
|
|
// and should always be a regular slash.
|
|
sep = "/"
|
|
)
|
|
|
|
// Root is the root of a tree. It contains a name and a subtree.
|
|
type Root struct {
|
|
Name string
|
|
Subtree *Tree
|
|
Prefix string
|
|
}
|
|
|
|
// New creates a new tree.
|
|
func New(name string) *Root {
|
|
return &Root{
|
|
Name: name,
|
|
Subtree: NewTree(),
|
|
}
|
|
}
|
|
|
|
// AddFile adds a new file to the tree.
|
|
func (r *Root) AddFile(path string, _ string) error {
|
|
return r.insert(path, false, "")
|
|
}
|
|
|
|
// AddMount adds a new mount point to the tree.
|
|
func (r *Root) AddMount(path, dest string) error {
|
|
return r.insert(path, false, dest)
|
|
}
|
|
|
|
// AddTemplate adds a template to the tree.
|
|
func (r *Root) AddTemplate(path string) error {
|
|
return r.insert(path, true, "")
|
|
}
|
|
|
|
func (r *Root) insert(path string, template bool, mountPath string) error {
|
|
t := r.Subtree
|
|
|
|
debug.V(4).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,
|
|
Subtree: NewTree(),
|
|
}
|
|
// this is the final element (a leaf)
|
|
if i == len(p)-1 {
|
|
n.Leaf = true
|
|
n.Subtree = nil
|
|
n.Template = template
|
|
|
|
if mountPath != "" {
|
|
n.Mount = true
|
|
n.Path = mountPath
|
|
}
|
|
}
|
|
|
|
debug.V(4).Log("[%d] %s -> Node: %+v", i, e, n)
|
|
|
|
node := t.Insert(n)
|
|
debug.V(4).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()
|
|
}
|
|
|
|
// re-root t to the new subtree
|
|
t = node.Subtree
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Format returns a pretty printed string of all nodes in and below
|
|
// this node, e.g. `├── baz`.
|
|
func (r *Root) Format(maxDepth int) string {
|
|
var sb strings.Builder
|
|
|
|
// any mount will be colored and include the on-disk path
|
|
_, _ = sb.WriteString(colDir(r.Name))
|
|
|
|
// finish this folders output
|
|
_, _ = sb.WriteString("\n")
|
|
|
|
// let our children format themselves
|
|
for i, node := range r.Subtree.Nodes {
|
|
last := i == len(r.Subtree.Nodes)-1
|
|
_, _ = sb.WriteString(node.format("", last, maxDepth, 1))
|
|
}
|
|
|
|
return sb.String()
|
|
}
|
|
|
|
// List returns a flat list of all files in this tree.
|
|
func (r *Root) List(maxDepth int) []string {
|
|
out := make([]string, 0, r.Len())
|
|
for _, t := range r.Subtree.Nodes {
|
|
out = append(out, t.list(r.Prefix, maxDepth, 0, true)...)
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
// ListFolders returns a flat list of all folders in this tree.
|
|
func (r *Root) ListFolders(maxDepth int) []string {
|
|
out := make([]string, 0, r.Len())
|
|
for _, t := range r.Subtree.Nodes {
|
|
out = append(out, t.list(r.Prefix, maxDepth, 0, false)...)
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
// String returns the name of this tree.
|
|
func (r *Root) String() string {
|
|
return r.Name
|
|
}
|
|
|
|
// FindFolder returns the subtree rooted at path.
|
|
func (r *Root) FindFolder(path string) (*Root, error) {
|
|
path = strings.TrimSuffix(path, "/")
|
|
t := r.Subtree
|
|
p := strings.Split(path, "/")
|
|
prefix := ""
|
|
|
|
for _, e := range p {
|
|
_, node := t.findPositionFor(e)
|
|
if node == nil || node.Subtree == nil {
|
|
return nil, ErrNotFound
|
|
}
|
|
|
|
t = node.Subtree
|
|
prefix = filepath.Join(prefix, e)
|
|
}
|
|
|
|
return &Root{Name: r.Name, Subtree: t, Prefix: prefix}, nil
|
|
}
|
|
|
|
// SetName changes the name of this tree.
|
|
func (r *Root) SetName(n string) {
|
|
r.Name = n
|
|
}
|
|
|
|
// Len returns the number of entries in this folder and all subfolder including
|
|
// this folder itself.
|
|
func (r *Root) Len() int {
|
|
var l int
|
|
for _, t := range r.Subtree.Nodes {
|
|
l += t.Len()
|
|
}
|
|
|
|
return l
|
|
}
|