diff --git a/README.md b/README.md index 1f41e73..6f04e2d 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ Prints a tree-like representation of the input. - `-c, --charset CHARSET`: Use CHARSET to display characters (utf-8, ascii). - `-s, --trailing-slash`: Display trailing slash on directory. - `-p, --full-path`: Display full path. +- `-r, --root-path`: Replace root with given path. Default: "." - `-D, --no-root-dot`: Do not display a root element. ## Installation diff --git a/args.go b/args.go index c7855df..b8c1fd8 100644 --- a/args.go +++ b/args.go @@ -4,7 +4,6 @@ import ( "embed" "fmt" "os" - "strings" ) //go:embed version.txt @@ -28,8 +27,8 @@ var VERSION embed.FS // Returns: // // Options - A struct containing the parsed options. -func getOpts() Options { - opts := Options{false, "", strings.Builder{}, "utf-8", false, false, true} +func getOpts() *Options { + opts := DefaultOptions() args := os.Args[1:] for len(args) > 0 { switch args[0] { @@ -83,6 +82,11 @@ func getOpts() Options { opts.rootDot = false args = args[1:] } + case "-r", "--root-path": + { + opts.rootPath = args[1] + args = args[2:] + } default: { opts.extra.WriteString(args[0] + "\n") diff --git a/describe.go b/describe.go index f704212..753a7c0 100644 --- a/describe.go +++ b/describe.go @@ -25,6 +25,7 @@ func helpText() strings.Builder { builder.WriteString(" -c, --charset CHARSET Use CHARSET to display characters (utf-8, ascii)" + LE) builder.WriteString(" -s, --trailing-slash Display trailing slash on directory" + LE) builder.WriteString(" -p, --full-path Display full path" + LE) + builder.WriteString(" -r, --root-path Replace root with given path. Default: \".\"" + LE) builder.WriteString(" -D, --no-root-dot Do not display a root element" + LE) return builder } @@ -172,8 +173,13 @@ func getName(node *Node, opts *Options) string { str := chunks.String() if opts.fullPath && node.parent != nil { - newOpts := Options{false, "", strings.Builder{}, opts.charset, true, opts.fullPath, opts.rootDot} - str = getName(node.parent, &newOpts) + str + newOpts := DefaultOptions() + newOpts.charset = opts.charset + newOpts.fullPath = opts.fullPath + newOpts.rootPath = opts.rootPath + newOpts.rootDot = opts.rootDot + newOpts.trailingSlash = true + str = getName(node.parent, newOpts) + str } return str diff --git a/parse.go b/parse.go index c0e4f63..ed1e07f 100644 --- a/parse.go +++ b/parse.go @@ -44,9 +44,13 @@ func parseDepth(line string, indentSize int) int { // Returns: // // *Node - The root node of the parsed tree structure. -func parseInput(input string) *Node { +func parseInput(input string, opts *Options) *Node { input = strings.Replace(input, "\r", "", -1) - root := &Node{".", 0, []*Node{}, nil} + rootName := "." + if opts.rootPath != "" && opts.rootPath != "." { + rootName = opts.rootPath + } + root := &Node{rootName, 0, []*Node{}, nil} current := root indentSize := 0 LE := LE_UNIX diff --git a/parser_test.go b/parser_test.go index d9d3eac..cb69652 100644 --- a/parser_test.go +++ b/parser_test.go @@ -24,7 +24,8 @@ func TestParseDepth(t *testing.T) { func TestParseInput(t *testing.T) { input := "root\n child1\n child2\n grandchild1\n" - root := parseInput(input) + opts := DefaultOptions() + root := parseInput(input, opts) if root.name != "." { t.Errorf("Expected root name to be '.', got %s", root.name) diff --git a/structs.go b/structs.go index e05c8cd..ecc7311 100644 --- a/structs.go +++ b/structs.go @@ -10,6 +10,21 @@ type Options struct { trailingSlash bool fullPath bool rootDot bool + rootPath string +} + +// default options factory +func DefaultOptions() *Options { + return &Options{ + fromStdin: false, + fromFile: "", + extra: strings.Builder{}, + charset: "utf-8", + trailingSlash: false, + fullPath: false, + rootDot: true, + rootPath: ".", + } } type Node struct { diff --git a/test_files/refresh.sh b/test_files/refresh.sh index f1e53be..b354bf3 100755 --- a/test_files/refresh.sh +++ b/test_files/refresh.sh @@ -1,11 +1,13 @@ #!/usr/bin/env bash +go install cd "$(dirname $0)/src" for i in test_*; do echo "Creating snapshots for $i" woext="${i%.*}" treelike -f "$i" -D > "../snapshots/${woext}_snapshot_no_root.txt" + treelike -f "$i" -r "~" -p > "../snapshots/${woext}_snapshot_root_path.txt" treelike -f "$i" -p > "../snapshots/${woext}_snapshot_full_path.txt" treelike -f "$i" -s > "../snapshots/${woext}_snapshot_trailing_slash.txt" treelike -f "$i" -c ascii > "../snapshots/${woext}_snapshot_ascii.txt" diff --git a/test_files/snapshots/test_1_snapshot_root_path.txt b/test_files/snapshots/test_1_snapshot_root_path.txt new file mode 100644 index 0000000..b8fea01 --- /dev/null +++ b/test_files/snapshots/test_1_snapshot_root_path.txt @@ -0,0 +1,7 @@ +~ +└── ~/a + ├── ~/a/b + │ └── ~/a/b/c + └── ~/a/d + ├── ~/a/d/e + └── ~/a/d/f diff --git a/test_files/snapshots/test_2_snapshot_root_path.txt b/test_files/snapshots/test_2_snapshot_root_path.txt new file mode 100644 index 0000000..11c1c3f --- /dev/null +++ b/test_files/snapshots/test_2_snapshot_root_path.txt @@ -0,0 +1,7 @@ +~ +└── ~/SceneBase (Node2D) + ├── ~/SceneBase (Node2D)/GroundLayer (TileMapLayer) + │ ├── ~/SceneBase (Node2D)/GroundLayer (TileMapLayer)/Player (CharacterBody2D) + │ └── ~/SceneBase (Node2D)/GroundLayer (TileMapLayer)/Enemy (CharacterBody2D) + ├── ~/SceneBase (Node2D)/Trees (TileMapLayer) + └── ~/SceneBase (Node2D)/Rocks (TileMapLayer) diff --git a/test_files/snapshots/test_3_snapshot_root_path.txt b/test_files/snapshots/test_3_snapshot_root_path.txt new file mode 100644 index 0000000..7160f8b --- /dev/null +++ b/test_files/snapshots/test_3_snapshot_root_path.txt @@ -0,0 +1,8 @@ +~ +└── ~/. + └── ~/./SceneBase (Node2D) + ├── ~/./SceneBase (Node2D)/GroundLayer (TileMapLayer) + │ ├── ~/./SceneBase (Node2D)/GroundLayer (TileMapLayer)/Player (CharacterBody2D) + │ └── ~/./SceneBase (Node2D)/GroundLayer (TileMapLayer)/Enemy (CharacterBody2D) + ├── ~/./SceneBase (Node2D)/Trees (TileMapLayer) + └── ~/./SceneBase (Node2D)/Rocks (TileMapLayer) diff --git a/test_files/snapshots/test_4_snapshot_root_path.txt b/test_files/snapshots/test_4_snapshot_root_path.txt new file mode 100644 index 0000000..c2462d2 --- /dev/null +++ b/test_files/snapshots/test_4_snapshot_root_path.txt @@ -0,0 +1,11 @@ +~ +└── ~/usr + ├── ~/usr/local + ├── ~/usr/bin + │ ├── ~/usr/bin/sh + │ ├── ~/usr/bin/bash + │ ├── ~/usr/bin/zsh + │ └── ~/usr/bin/fish + └── ~/usr/sbin + ├── ~/usr/sbin/sysctl + └── ~/usr/sbin/tcpdump diff --git a/test_files/snapshots/test_5_snapshot_root_path.txt b/test_files/snapshots/test_5_snapshot_root_path.txt new file mode 100644 index 0000000..89da56f --- /dev/null +++ b/test_files/snapshots/test_5_snapshot_root_path.txt @@ -0,0 +1,9 @@ +~ +├── ~/I +│ └── ~/I/am +│ └── ~/I/am/a +│ └── ~/I/am/a/superhero! +├── ~/a +│ └── ~/a/what? +└── ~/a + └── ~/a/superhero! diff --git a/test_files/snapshots/test_6_snapshot_root_path.txt b/test_files/snapshots/test_6_snapshot_root_path.txt new file mode 100644 index 0000000..89da56f --- /dev/null +++ b/test_files/snapshots/test_6_snapshot_root_path.txt @@ -0,0 +1,9 @@ +~ +├── ~/I +│ └── ~/I/am +│ └── ~/I/am/a +│ └── ~/I/am/a/superhero! +├── ~/a +│ └── ~/a/what? +└── ~/a + └── ~/a/superhero! diff --git a/tree_test.go b/tree_test.go index 2f5af54..5fe7aea 100644 --- a/tree_test.go +++ b/tree_test.go @@ -8,9 +8,10 @@ import ( func TestDescribeTree(t *testing.T) { input := "root\n child1\n child2\n grandchild1\n" - root := parseInput(input) - opts := Options{rootDot: true} - result := describeTree(root, &opts) + opts := DefaultOptions() + opts.rootDot = true + root := parseInput(input, opts) + result := describeTree(root, opts) expected := ".\n└── root\n ├── child1\n └── child2\n └── grandchild1" if result != expected { @@ -19,8 +20,8 @@ func TestDescribeTree(t *testing.T) { } func TestRemovePrefix(t *testing.T) { - opts := Options{false, "", strings.Builder{}, "utf-8", false, false, true} - CHILD, LAST_CHILD, DIRECTORY, EMPTY := getPrefixes(&opts) + opts := DefaultOptions() + CHILD, LAST_CHILD, DIRECTORY, EMPTY := getPrefixes(opts) tests := []struct { str string @@ -33,7 +34,7 @@ func TestRemovePrefix(t *testing.T) { } for _, test := range tests { - result := removePrefix(test.str, &opts) + result := removePrefix(test.str, opts) if result != test.expected { t.Errorf("removePrefix(%q)\n actual = %q\nwant = %q", test.str, result, test.expected) } @@ -71,9 +72,10 @@ func TestGetPrefixes(t *testing.T) { func TestMultiRoot(t *testing.T) { input := "I\n am\n a\n superhero!\na\n what?\na\n superhero!\n" - root := parseInput(input) - opts := Options{rootDot: true} - result := describeTree(root, &opts) + opts := DefaultOptions() + opts.rootDot = true + root := parseInput(input, opts) + result := describeTree(root, opts) expected := ".\n├── I\n│ └── am\n│ └── a\n│ └── superhero!\n├── a\n│ └── what?\n└── a\n └── superhero!" if result != expected { t.Errorf("describeTree()\n actual = %q\nwant = %q", result, expected) @@ -82,9 +84,10 @@ func TestMultiRoot(t *testing.T) { func TestWinNewLines(t *testing.T) { input := "root\r\n child1\r\n child2\r\n grandchild1\r\n" - root := parseInput(input) - opts := Options{rootDot: true} - result := describeTree(root, &opts) + opts := DefaultOptions() + opts.rootDot = true + root := parseInput(input, opts) + result := describeTree(root, opts) expected := ".\n└── root\n ├── child1\n └── child2\n └── grandchild1" if result != expected { t.Errorf("describeTree()\n actual = %q\nwant = %q", result, expected) @@ -118,13 +121,18 @@ func TestSnapshots(t *testing.T) { tests := make(map[string]*Options) - // &Options{fromStdin, fromFile, extra, charset, trailingSlash, fullPath, rootDot} - - tests[""] = &Options{false, "", strings.Builder{}, "utf-8", false, false, true} - tests["_ascii"] = &Options{false, "", strings.Builder{}, "ascii", false, false, true} - tests["_full_path"] = &Options{false, "", strings.Builder{}, "utf-8", false, true, true} - tests["_no_root"] = &Options{false, "", strings.Builder{}, "utf-8", false, false, false} - tests["_trailing_slash"] = &Options{false, "", strings.Builder{}, "utf-8", true, false, true} + tests[""] = DefaultOptions() + tests["_ascii"] = DefaultOptions() + tests["_ascii"].charset = "ascii" + tests["_full_path"] = DefaultOptions() + tests["_full_path"].fullPath = true + tests["_root_path"] = DefaultOptions() + tests["_root_path"].rootPath = "~" + tests["_root_path"].fullPath = true + tests["_no_root"] = DefaultOptions() + tests["_no_root"].rootDot = false + tests["_trailing_slash"] = DefaultOptions() + tests["_trailing_slash"].trailingSlash = true contents, err := os.ReadFile(dirPath + string(os.PathSeparator) + entry.Name()) @@ -143,7 +151,7 @@ func TestSnapshots(t *testing.T) { t.Errorf("Error reading file: %v", err) break } - actual := describeTree(parseInput(string(contents)), opts) + actual := describeTree(parseInput(string(contents), opts), opts) if actual+"\n" != string(want) { t.Errorf("describeTree()\nactual = %q\nwant = %q\n file %v", actual, want, filePath) diff --git a/treelike.go b/treelike.go index 4de78c3..17dce40 100644 --- a/treelike.go +++ b/treelike.go @@ -8,11 +8,11 @@ import ( func main() { opts := getOpts() - input, err, code := parseRawInput(&opts) + input, err, code := parseRawInput(opts) if err != nil { fmt.Println(err) os.Exit(code) } - node := parseInput(input.String()) - fmt.Println(describeTree(node, &opts)) + node := parseInput(input.String(), opts) + fmt.Println(describeTree(node, opts)) }