aboutsummaryrefslogtreecommitdiffstats
path: root/cmd
diff options
context:
space:
mode:
authorJP Appel <jeanpierre.appel01@gmail.com>2025-07-23 02:07:08 -0400
committerJP Appel <jeanpierre.appel01@gmail.com>2025-07-23 02:07:08 -0400
commitf949689b4d56daa9988c821e2f0e1b470cfc7275 (patch)
tree30cd47a5bd6a6598c4b10a107e72767c36262522 /cmd
parent8633ab4bc13bf957d7598700338c8d0e251e0cfa (diff)
Update help info
Diffstat (limited to 'cmd')
-rw-r--r--cmd/help.go155
-rw-r--r--cmd/index.go22
-rw-r--r--cmd/query.go77
-rw-r--r--cmd/server.go2
-rw-r--r--cmd/shell.go28
5 files changed, 182 insertions, 102 deletions
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] <command>\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] <command>\n\n", os.Args[0])
+ fmt.Fprintln(w, "Commands:")
+ fmt.Fprintln(w, " index <subcommand> - build, update, or modify an index")
+ fmt.Fprintln(w, " query <subcommand> - 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 <help-topic> - 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] <subcommand>\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 <subcommand> 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] <query>...\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'
+ "<h1><a href="%p">%T</a></h1>" -> '<h1><a href="/a/path/to/document">A Title</a></h1>\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] <subcommand>\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'
- "<h1><a href="%p">%T</a></h1>" -> '<h1><a href="/a/path/to/document">A Title</a></h1>\n'
-
-`
- fmt.Fprint(f, help)
- fmt.Fprintln(f, "Global Flags:")
- flag.PrintDefaults()
+ w := fs.Output()
+ fmt.Fprintf(w, "%s [global-flags] query [query-flags] <query>...\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
+}