diff --git a/cmd/cheat/cmd_tags.go b/cmd/cheat/cmd_tags.go new file mode 100644 index 0000000..a17c87c --- /dev/null +++ b/cmd/cheat/cmd_tags.go @@ -0,0 +1,25 @@ +package main + +import ( + "fmt" + "os" + + "github.com/cheat/cheat/internal/config" + "github.com/cheat/cheat/internal/sheets" +) + +// cmdTags lists all tags in use. +func cmdTags(opts map[string]interface{}, conf config.Config) { + + // load the cheatsheets + cheatsheets, err := sheets.Load(conf.Cheatpaths) + if err != nil { + fmt.Fprintln(os.Stderr, fmt.Sprintf("failed to list cheatsheets: %v", err)) + os.Exit(1) + } + + // write sheet tags to stdout + for _, tag := range sheets.Tags(cheatsheets) { + fmt.Println(tag) + } +} diff --git a/cmd/cheat/docopt.txt b/cmd/cheat/docopt.txt index effe2cc..4b4d87b 100644 --- a/cmd/cheat/docopt.txt +++ b/cmd/cheat/docopt.txt @@ -11,6 +11,7 @@ Options: -r --regex Treat search as a regex -s --search= Search cheatsheets for -t --tag= Return only sheets matching + -T --tags List all tags in use -v --version Print the version number Examples: @@ -33,6 +34,9 @@ Examples: To list all available cheatsheets: cheat -l + To list all tags in use: + cheat -T + To list available cheatsheets that are tagged as "personal": cheat -l -t personal diff --git a/cmd/cheat/main.go b/cmd/cheat/main.go index a4608ab..c36b569 100755 --- a/cmd/cheat/main.go +++ b/cmd/cheat/main.go @@ -76,6 +76,9 @@ func main() { case opts["--list"].(bool): cmd = cmdList + case opts["--tags"].(bool): + cmd = cmdTags + case opts["--search"] != nil: cmd = cmdSearch diff --git a/cmd/cheat/str_usage.go b/cmd/cheat/str_usage.go index 49baa38..482df0f 100644 --- a/cmd/cheat/str_usage.go +++ b/cmd/cheat/str_usage.go @@ -20,6 +20,7 @@ Options: -r --regex Treat search as a regex -s --search= Search cheatsheets for -t --tag= Return only sheets matching + -T --tags List all tags in use -v --version Print the version number Examples: diff --git a/internal/sheets/tags.go b/internal/sheets/tags.go new file mode 100644 index 0000000..1d7b0e7 --- /dev/null +++ b/internal/sheets/tags.go @@ -0,0 +1,36 @@ +package sheets + +import ( + "sort" + + "github.com/cheat/cheat/internal/sheet" +) + +// Tags returns a slice of all tags in use in any sheet +func Tags(cheatpaths []map[string]sheet.Sheet) []string { + + // create a map of all tags in use in any sheet + tags := make(map[string]bool) + + // iterate over all tags on all sheets on all cheatpaths + for _, path := range cheatpaths { + for _, sheet := range path { + for _, tag := range sheet.Tags { + tags[tag] = true + } + } + } + + // restructure the map into a slice + sorted := []string{} + for tag := range tags { + sorted = append(sorted, tag) + } + + // sort the slice + sort.Slice(sorted, func(i, j int) bool { + return sorted[i] < sorted[j] + }) + + return sorted +} diff --git a/internal/sheets/tags_test.go b/internal/sheets/tags_test.go new file mode 100644 index 0000000..54d156b --- /dev/null +++ b/internal/sheets/tags_test.go @@ -0,0 +1,51 @@ +package sheets + +import ( + "reflect" + "testing" + + "github.com/davecgh/go-spew/spew" + + "github.com/cheat/cheat/internal/sheet" +) + +// TestTags asserts that cheetsheet tags are properly returned +func TestTags(t *testing.T) { + + // mock cheatsheets available on multiple cheatpaths + cheatpaths := []map[string]sheet.Sheet{ + + // mock community cheatsheets + map[string]sheet.Sheet{ + "foo": sheet.Sheet{Title: "foo", Tags: []string{"alpha"}}, + "bar": sheet.Sheet{Title: "bar", Tags: []string{"alpha", "bravo"}}, + }, + + // mock local cheatsheets + map[string]sheet.Sheet{ + "bar": sheet.Sheet{Title: "bar", Tags: []string{"bravo", "charlie"}}, + "baz": sheet.Sheet{Title: "baz", Tags: []string{"delta"}}, + }, + } + + // consolidate the cheatsheets + tags := Tags(cheatpaths) + + // specify the expected output + want := []string{ + "alpha", + "bravo", + "charlie", + "delta", + } + + // assert that the cheatsheets properly consolidated + if !reflect.DeepEqual(tags, want) { + t.Errorf( + "failed to return tags: want:\n%s, got:\n%s", + spew.Sdump(want), + spew.Sdump(tags), + ) + } + +}