From f949689b4d56daa9988c821e2f0e1b470cfc7275 Mon Sep 17 00:00:00 2001 From: JP Appel Date: Wed, 23 Jul 2025 02:07:08 -0400 Subject: Update help info --- cmd/help.go | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++------- cmd/index.go | 22 +++------ cmd/query.go | 77 ++++------------------------- cmd/server.go | 2 +- cmd/shell.go | 28 +++++++++++ 5 files changed, 182 insertions(+), 102 deletions(-) create mode 100644 cmd/shell.go (limited to 'cmd') diff --git a/cmd/help.go b/cmd/help.go index b3844ac..e57ce2f 100644 --- a/cmd/help.go +++ b/cmd/help.go @@ -1,29 +1,146 @@ package cmd import ( + "flag" "fmt" + "io" "os" + + "github.com/jpappel/atlas/pkg/shell" + "github.com/jpappel/atlas/pkg/util" ) -var CommandHelp map[string]string - -func PrintHelp() { - fmt.Println("atlas is a note indexing and querying tool") - fmt.Printf("\nUsage:\n %s [global-flags] \n\n", os.Args[0]) - fmt.Println("Commands:") - fmt.Println(" index - build, update, or modify an index") - fmt.Println(" query - search against an index") - fmt.Println(" shell - start a debug shell") - fmt.Println(" server - start an http query server (EXPERIMENTAL)") - fmt.Println(" help - print this help then exit") +var helpTopics = []string{ + "index", "i", + "index build", "i build", + "index update", "i update", + "index tidy", "i tidy", + "query", "q", + "shell", + "server", +} + +func PrintHelp(w io.Writer) { + fmt.Fprintln(w, "atlas is a note indexing and querying tool") + fmt.Fprintf(w, "\nUsage:\n %s [global-flags] \n\n", os.Args[0]) + fmt.Fprintln(w, "Commands:") + fmt.Fprintln(w, " index - build, update, or modify an index") + fmt.Fprintln(w, " query - search against an index") + fmt.Fprintln(w, " shell - start a debug shell") + fmt.Fprintln(w, " server - start an http query server (EXPERIMENTAL)") + fmt.Fprintln(w, " help - print help info") } -func init() { - CommandHelp = make(map[string]string) - CommandHelp["query"] = "" - CommandHelp["index"] = "" - CommandHelp["server"] = "" - CommandHelp["completions"] = "" - CommandHelp["shell"] = "" - CommandHelp["help"] = "" +func PrintGlobalFlags(w io.Writer) { + fmt.Fprintln(w, "\nGlobal Flags:") + PrintFlagSet(w, flag.CommandLine) +} + +func PrintFlagSet(w io.Writer, fs *flag.FlagSet) { + w_ := fs.Output() + fs.SetOutput(w) + fs.PrintDefaults() + fs.SetOutput(w_) +} + +func Help(topic string, w io.Writer) { + fs := flag.NewFlagSet(topic, flag.ExitOnError) + switch topic { + case "index", "i": + SetupIndexFlags(nil, fs, &IndexFlags{}) + fmt.Fprintf(w, "%s [global-flags] index [index-flags] \n\n", os.Args[0]) + fmt.Fprintln(w, "Subcommands:") + fmt.Fprintln(w, " build - create a new index") + fmt.Fprintln(w, " update - update an existing index") + fmt.Fprintln(w, " tidy - cleanup an index") + fmt.Fprintf(w, "\nSee %s help index for subcommand help\n\n", os.Args[0]) + fmt.Fprintln(w, "Index Flags:") + PrintFlagSet(w, fs) + case "i build", "index build": + fmt.Fprintf(w, "%s [global-flags] index [index-flags] build\n\n", os.Args[0]) + fmt.Fprintln(w, "Crawl files starting at `-root` to build an index stored in `-db`") + fmt.Fprintln(w, "Use this subcommand to generate the initial index, then update it with `atlas index update`") + case "i update", "index update": + fmt.Fprintf(w, "%s [global-flags] index [index-flags] update\n\n", os.Args[0]) + fmt.Fprintln(w, "Crawl files starting at `-root` to update an index stored in `-db`") + fmt.Fprintln(w, "Use this subcommand to update an existing index.") + fmt.Fprintln(w, "Deleted documents are removed from the index. To remove unused authors and tags run `atlas index tidy`") + case "i tidy", "index tidy": + fmt.Fprintf(w, "%s [global-flags] index tidy\n\n", os.Args[0]) + fmt.Fprintln(w, "Remove unused authors or tags and optimize the database") + case "query", "q": + SetupQueryFlags(nil, fs, &QueryFlags{}, "") + fmt.Fprintf(w, "%s [global-flags] query [query-flags] ...\n\n", os.Args[0]) + fmt.Fprintln(w, "Execute a query against the connected database") + fmt.Fprintln(w, "Query Flags:") + PrintFlagSet(w, fs) + fmt.Fprintln(w, "\nOutput Format:") + help := `The output format of query results can be customized by setting -outCustomFormat. + + The output of each document has the value of -docSeparator appended to it. + Dates are formated using -dateFormat + Lists use -listSeparator to delimit elements + + Placeholder - Type - Value + %p - Str - path + %T - Str - title + %d - Date - date + %f - Date - filetime + %a - List - authors + %t - List - tags + %l - List - links + %m - Str - meta + + Examples: + "%p %T %d tags:%t" -> '/a/path/to/document A Title 2006-01-02T15:04:05Z07:00 tags:tag1, tag2\n' + "

%T

" -> '

A Title

\n' + +` + fmt.Fprint(w, help) + case "shell": + fmt.Fprintf(w, "%s [global-flags] shell\n", os.Args[0]) + fmt.Fprintln(w, "Simple shell for debugging queries") + fmt.Fprintln(w, "\nShell Help:") + shell.PrintHelp(w) + case "server": + SetupServerFlags(nil, fs, &ServerFlags{}) + fmt.Fprintf(w, "%s [global-flags] server [server-flags]", os.Args[0]) + fmt.Fprintln(w, "Run a server to execute queries over HTTP or a unix domain socket") + fmt.Fprintln(w, "HTTP Server:") + fmt.Fprintln(w, " To execute a query POST it in the request body to /search") + fmt.Fprintln(w, " ex. curl -d 'T:notes d>=\"January 1, 2025\"' 127.0.0.1:8080/search") + fmt.Fprintln(w, " To have the backend use the query params `sortBy` and `sortOrder`") + fmt.Fprintln(w, " sortBy: path, title, date, filetime, meta") + fmt.Fprintln(w, " sortOrder: desc, descending") + fmt.Fprintln(w, "Server Flags:") + PrintFlagSet(w, fs) + case "help", "": + PrintHelp(w) + fmt.Fprintln(w, "\nHelp Topics:") + curLineLen := 2 + fmt.Fprint(w, " ") + for i, topic := range helpTopics { + if curLineLen+len(topic) < 80 { + curLineLen += len(topic) + fmt.Fprint(w, topic) + } else { + fmt.Fprintln(w, topic) + fmt.Fprint(w, " ") + curLineLen = 2 + } + if i == len(helpTopics)-1 { + fmt.Fprintln(w) + } else if curLineLen != 2 { + fmt.Fprint(w, ", ") + curLineLen += 3 + } + } + PrintGlobalFlags(w) + default: + fmt.Fprintln(os.Stderr, "Unrecognized topic: ", topic) + if suggestion, ok := util.Nearest(topic, helpTopics, util.LevensteinDistance, 3); ok { + fmt.Fprintf(w, "Did you mean %s?\n", suggestion) + } + fmt.Fprintln(w, "See `atlas help`") + } } diff --git a/cmd/index.go b/cmd/index.go index 5454a8a..358182c 100644 --- a/cmd/index.go +++ b/cmd/index.go @@ -23,7 +23,7 @@ func SetupIndexFlags(args []string, fs *flag.FlagSet, flags *IndexFlags) { flags.ParseMeta = true fs.BoolVar(&flags.IgnoreDateError, "ignoreBadDates", false, "ignore malformed dates while indexing") fs.BoolVar(&flags.IgnoreMetaError, "ignoreMetaError", false, "ignore errors while parsing general YAML header info") - fs.BoolFunc("ignoreMeta", "don't parse YAML header values other title, authors, date, tags", func(s string) error { + fs.BoolFunc("ignoreMeta", "only parse title, authors, date, tags from YAML headers", func(s string) error { flags.ParseMeta = false return nil }) @@ -33,20 +33,6 @@ func SetupIndexFlags(args []string, fs *flag.FlagSet, flags *IndexFlags) { }) fs.BoolVar(&flags.IgnoreHidden, "ignoreHidden", false, "ignore hidden files while crawling") - fs.Usage = func() { - f := fs.Output() - fmt.Fprintf(f, "Usage of %s %s\n", os.Args[0], fs.Name()) - fmt.Fprintf(f, " %s [global-flags] %s [index-flags] \n\n", os.Args[0], fs.Name()) - fmt.Fprintln(f, "Subcommands:") - fmt.Fprintln(f, "build - create a new index") - fmt.Fprintln(f, "update - update an existing index") - fmt.Fprintln(f, "tidy - cleanup an index") - fmt.Fprintln(f, "\nIndex Flags:") - fs.PrintDefaults() - fmt.Fprintln(f, "\nGlobal Flags:") - flag.PrintDefaults() - } - customFilters := false flags.Filters = index.DefaultFilters() fs.Func("filter", @@ -67,6 +53,12 @@ func SetupIndexFlags(args []string, fs *flag.FlagSet, flags *IndexFlags) { return nil }) + fs.Usage = func() { + f := fs.Output() + Help("index", f) + PrintGlobalFlags(f) + } + fs.Parse(args) remainingArgs := fs.Args() diff --git a/cmd/query.go b/cmd/query.go index d00a792..4b28e09 100644 --- a/cmd/query.go +++ b/cmd/query.go @@ -3,10 +3,8 @@ package cmd import ( "flag" "fmt" - "log/slog" "os" "slices" - "strings" "github.com/jpappel/atlas/pkg/data" "github.com/jpappel/atlas/pkg/index" @@ -47,43 +45,17 @@ func SetupQueryFlags(args []string, fs *flag.FlagSet, flags *QueryFlags, dateFor fs.StringVar(&flags.SortBy, "sortBy", "", "category to sort by (path,title,date,filetime,meta)") fs.BoolVar(&flags.SortDesc, "sortDesc", false, "sort in descending order") - fs.StringVar(&flags.CustomFormat, "outCustomFormat", query.DefaultOutputFormat, "format string for --outFormat custom, see Output Format for more details") + fs.StringVar(&flags.CustomFormat, "outCustomFormat", query.DefaultOutputFormat, "`format` string for --outFormat custom, see `atlas help query` for more details") fs.IntVar(&flags.OptimizationLevel, "optLevel", 0, "optimization `level` for queries, 0 is automatic, <0 to disable") fs.StringVar(&flags.DocumentSeparator, "docSeparator", "\n", "separator for custom output format") fs.StringVar(&flags.ListSeparator, "listSeparator", ", ", "separator for list fields") fs.Usage = func() { - f := fs.Output() - fmt.Fprintf(f, "Usage of %s %s\n", os.Args[0], fs.Name()) - fmt.Fprintf(f, " %s [global-flags] %s [query-flags]\n\n", - os.Args[0], fs.Name()) - fmt.Fprintln(f, "Query Flags:") - fs.PrintDefaults() - fmt.Fprintln(f, "\nOutput Format:") - help := `The output format of query results can be customized by setting -outCustomFormat. - - The output of each document has the value of -docSeparator appended to it. - Dates are formated using -dateFormat - Lists use -listSeparator to delimit elements - - Placeholder - Type - Value - %p - Str - path - %T - Str - title - %d - Date - date - %f - Date - filetime - %a - List - authors - %t - List - tags - %l - List - links - %m - Str - meta - - Examples: - "%p %T %d tags:%t" -> '/a/path/to/document A Title 2006-01-02T15:04:05Z07:00 tags:tag1, tag2\n' - "

%T

" -> '

A Title

\n' - -` - fmt.Fprint(f, help) - fmt.Fprintln(f, "Global Flags:") - flag.PrintDefaults() + w := fs.Output() + fmt.Fprintf(w, "%s [global-flags] query [query-flags] ...\n\n", os.Args[0]) + fmt.Fprintln(w, "Query Flags:") + PrintFlagSet(w, fs) + PrintGlobalFlags(w) } fs.Parse(args) @@ -122,40 +94,11 @@ func RunQuery(gFlags GlobalFlags, qFlags QueryFlags, db *data.Query, searchQuery outputableResults = append(outputableResults, v) } - var docCmp func(a, b *index.Document) int - descMod := 1 - if qFlags.SortDesc { - descMod = -1 - } - switch qFlags.SortBy { - case "": - case "path": - docCmp = func(a, b *index.Document) int { - return descMod * strings.Compare(a.Path, b.Path) - } - case "title": - docCmp = func(a, b *index.Document) int { - return descMod * strings.Compare(a.Title, b.Title) - } - case "date": - docCmp = func(a, b *index.Document) int { - return descMod * a.Date.Compare(b.Date) - } - case "filetime": - docCmp = func(a, b *index.Document) int { - return descMod * a.FileTime.Compare(b.FileTime) - } - case "meta": - docCmp = func(a, b *index.Document) int { - return descMod * strings.Compare(a.OtherMeta, b.OtherMeta) - } - default: - slog.Error("Unrecognized category to sort by, leaving documents unsorted") - qFlags.SortBy = "" - } - if qFlags.SortBy != "" { - slices.SortFunc(outputableResults, docCmp) + docCmp, ok := index.NewDocCmp(qFlags.SortBy, qFlags.SortDesc) + if ok { + slices.SortFunc(outputableResults, docCmp) + } } _, err = qFlags.Outputer.OutputTo(os.Stdout, outputableResults) diff --git a/cmd/server.go b/cmd/server.go index 6182221..45fe197 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -22,7 +22,7 @@ type ServerFlags struct { } func SetupServerFlags(args []string, fs *flag.FlagSet, flags *ServerFlags) { - fs.StringVar(&flags.Address, "address", "", "the address to listen on, prefix with 'unix:' to create a unixsocket") + fs.StringVar(&flags.Address, "address", "127.0.0.1", "the address to listen on, prefix with 'unix:' to create a unixsocket") fs.IntVar(&flags.Port, "port", 8080, "the port to bind to") fs.Parse(args) diff --git a/cmd/shell.go b/cmd/shell.go new file mode 100644 index 0000000..218ecd0 --- /dev/null +++ b/cmd/shell.go @@ -0,0 +1,28 @@ +package cmd + +import ( + "fmt" + "io" + "log/slog" + + "github.com/jpappel/atlas/pkg/data" + "github.com/jpappel/atlas/pkg/shell" +) + +func RunShell(gFlags GlobalFlags, db *data.Query, version string) byte { + state := make(shell.State) + env := make(map[string]string) + + env["workers"] = fmt.Sprint(gFlags.NumWorkers) + env["db_path"] = gFlags.DBPath + env["index_root"] = gFlags.IndexRoot + env["version"] = version + + interpreter := shell.NewInterpreter(state, env, gFlags.NumWorkers, db) + if err := interpreter.Run(); err != nil && err != io.EOF { + slog.Error("Fatal error occured", slog.String("err", err.Error())) + return 1 + } + + return 0 +} -- cgit v1.2.3