aboutsummaryrefslogtreecommitdiffstats
path: root/cmd/atlas.go
diff options
context:
space:
mode:
authorJP Appel <jeanpierre.appel01@gmail.com>2025-06-13 00:22:17 -0400
committerJP Appel <jeanpierre.appel01@gmail.com>2025-06-13 00:22:17 -0400
commitbf841d00967c3047bff88dbe070cb56319e1fe38 (patch)
treeb6c00e633aa4a0f15632a05b2c60995b314a7ba3 /cmd/atlas.go
parentc89447a77e816447e84ceeeb653053f8450d9360 (diff)
Refactor commandline flag and argument parsing
Diffstat (limited to 'cmd/atlas.go')
-rw-r--r--cmd/atlas.go194
1 files changed, 167 insertions, 27 deletions
diff --git a/cmd/atlas.go b/cmd/atlas.go
index cbc21d4..a6d161a 100644
--- a/cmd/atlas.go
+++ b/cmd/atlas.go
@@ -1,62 +1,202 @@
package main
import (
+ "context"
"flag"
"fmt"
"log/slog"
"os"
+ "runtime"
+ "strings"
+ "time"
"github.com/jpappel/atlas/pkg/data"
"github.com/jpappel/atlas/pkg/index"
+ "github.com/jpappel/atlas/pkg/query"
)
-const ExitCommand = 2 // exit because of a command parsing error
+const ExitCommand = 2 // exit because of a command parsing error
+const dateFormat = time.RFC3339 // TODO: make a flag
-var commands = []string{"query", "index", "version", "help"}
+type GlobalFlags struct {
+ IndexRoot string
+ DBPath string
+ LogLevel string
+ LogJson bool
+ NumWorkers uint
+}
+
+func addGlobalFlagUsage(fs *flag.FlagSet) func() {
+ return func() {
+ f := fs.Output()
+ fmt.Fprintln(f, "Usage of", fs.Name())
+ fs.PrintDefaults()
+ fmt.Fprintln(f, "\nGlobal Flags:")
+ flag.PrintDefaults()
+ }
+}
+
+func printHelp() {
+ fmt.Println("atlas is a note indexing and querying tool")
+ fmt.Printf("\nUsage:\n %s <command>\n\n", os.Args[0])
+ fmt.Println("Commands:")
+ fmt.Println(" index - build, update, or modify the index")
+ fmt.Println(" query - search against the index")
+ fmt.Println(" help - print this help then exit")
+}
func main() {
- // global opts
- indexRoot := flag.String("root", "/home/goose/src/atlas/test", "root directory for indexing")
- docDB := flag.String("db", "/home/goose/src/atlas/test.db", "path to document database")
- logLevel := flag.String("logLevel", "error", "set log level (debug, info, warn, error)")
+ globalFlags := GlobalFlags{}
+ flag.StringVar(&globalFlags.IndexRoot, "root", "/home/goose/src/atlas/test", "root `directory` for indexing")
+ flag.StringVar(&globalFlags.DBPath, "db", "/home/goose/src/atlas/test.db", "`path` to document database")
+ flag.StringVar(&globalFlags.LogLevel, "logLevel", "error", "set log `level` (debug, info, warn, error)")
+ flag.BoolVar(&globalFlags.LogJson, "logJson", false, "log to json")
+ flag.UintVar(&globalFlags.NumWorkers, "numWorkers", uint(runtime.NumCPU()), "number of worker threads to use (defaults to core count)")
- // command specific opts
- // TODO: parse a list of fitlers
- docFilters := index.DefaultFilters()
+ indexFs := flag.NewFlagSet("index flags", flag.ExitOnError)
+ queryFs := flag.NewFlagSet("query flags", flag.ExitOnError)
+
+ indexFs.Usage = addGlobalFlagUsage(indexFs)
+ queryFs.Usage = addGlobalFlagUsage(queryFs)
flag.Parse()
+ args := flag.Args()
+
+ queryFlags := struct {
+ Output query.Outputer
+ CustomFormat string
+ }{}
+ indexFlags := struct {
+ ignoreBadDates bool
+ Filters []index.DocFilter
+ }{}
+
+ if len(args) < 2 {
+ fmt.Fprintln(os.Stderr, "No Command provided")
+ printHelp()
+ fmt.Fprintln(flag.CommandLine.Output(), "\nGlobal Flags:")
+ flag.PrintDefaults()
+ os.Exit(ExitCommand)
+ }
+ command := args[0]
+
+ switch command {
+ case "query":
+ // NOTE: providing `-outFormat` before `-outCustomFormat` might ignore user specified format
+ queryFs.Func("outFormat", "output `format` for queries (default, json, custom)",
+ func(arg string) error {
+ if arg == "default" {
+ queryFlags.Output = query.DefaultOutput{}
+ return nil
+ } else if arg == "json" {
+ queryFlags.Output = query.JsonOutput{}
+ return nil
+ } else if arg == "custom" {
+ var err error
+ queryFlags.Output, err = query.NewCustomOutput(queryFlags.CustomFormat, dateFormat)
+ return err
+ }
+ return fmt.Errorf("Unrecognized output format: %s", arg)
+ })
+ queryFs.StringVar(&queryFlags.CustomFormat, "outCustomFormat", query.DefaultOutputFormat, "format string for --outFormat custom, see EXAMPLES for more details")
+
+ queryFs.Parse(args[1:])
+ case "index":
+ indexFs.BoolVar(&indexFlags.ignoreBadDates, "ignoreBadDates", false, "ignore malformed dates while indexing")
+
+ customFilters := false
+ indexFlags.Filters = index.DefaultFilters()
+ indexFs.Func("filter",
+ "accept or reject files from indexing, applied in supplied order"+
+ "\n(default Ext_.md, MaxSize_204800, YAMLHeader, ExcludeParent_templates)\n"+
+ index.FilterHelp,
+ func(s string) error {
+ if !customFilters {
+ indexFlags.Filters = indexFlags.Filters[:0]
+ }
+
+ filter, err := index.ParseFilter(s)
+ if err != nil {
+ return err
+ }
+ indexFlags.Filters = append(indexFlags.Filters, filter)
+
+ return nil
+ })
+
+ indexFs.Parse(args[1:])
+ case "help", "--help", "-help":
+ printHelp()
+ flag.PrintDefaults()
+ os.Exit(0)
+ default:
+ fmt.Fprintln(os.Stderr, "Unrecognized command: ", command)
+ printHelp()
+ os.Exit(ExitCommand)
+ }
slogLevel := &slog.LevelVar{}
- if *logLevel == "debug" {
+ switch globalFlags.LogLevel {
+ case "debug":
slogLevel.Set(slog.LevelDebug)
- } else if *logLevel == "info" {
+ case "info":
slogLevel.Set(slog.LevelInfo)
- } else if *logLevel == "warn" {
+ case "warn":
slogLevel.Set(slog.LevelWarn)
- } else if *logLevel == "error" {
+ case "error":
slogLevel.Set(slog.LevelError)
- } else {
- fmt.Fprintln(os.Stderr, "Unrecognized log level:", *logLevel)
+ default:
+ fmt.Fprintln(os.Stderr, "Unrecognized log level:", globalFlags.LogLevel)
os.Exit(ExitCommand)
}
loggerOpts := &slog.HandlerOptions{Level: slogLevel}
- logger := slog.New(slog.NewTextHandler(os.Stderr, loggerOpts))
+ var logHandler slog.Handler
+ if globalFlags.LogJson {
+ logHandler = slog.NewJSONHandler(os.Stderr, loggerOpts)
+ } else {
+ logHandler = slog.NewTextHandler(os.Stderr, loggerOpts)
+ }
+ logger := slog.New(logHandler)
slog.SetDefault(logger)
- query := data.NewQuery(*docDB)
- defer query.Close()
+ querier := data.NewQuery(globalFlags.DBPath)
+ defer querier.Close()
+
+ // command specific
+ switch command {
+ case "query":
+ // TODO: evaluate query
+ s, err := queryFlags.Output.Output(nil)
+ if err != nil {
+ slog.Error("Error while outputing query results", slog.String("err", err.Error()))
+ return
+ }
+ fmt.Print(s)
+ case "index":
+ idx := index.Index{Root: globalFlags.IndexRoot, Filters: indexFlags.Filters}
+ if logger.Enabled(context.Background(), slog.LevelDebug) {
+ filterNames := make([]string, 0, len(indexFlags.Filters))
+ for _, filter := range indexFlags.Filters {
+ filterNames = append(filterNames, filter.Name)
+ }
+ logger.Debug("index",
+ slog.String("indexRoot", globalFlags.IndexRoot),
+ slog.String("filters", strings.Join(filterNames, ", ")),
+ )
+ }
- idx := index.Index{Root: *indexRoot, Filters: docFilters}
- fmt.Println("index:", idx)
+ traversedFiles := idx.Traverse(globalFlags.NumWorkers)
+ fmt.Print("Crawled ", len(traversedFiles))
- traversedFiles := idx.Traverse(4)
- fmt.Println("traversed files:", traversedFiles)
+ filteredFiles := idx.Filter(traversedFiles, globalFlags.NumWorkers)
+ fmt.Print(", Filtered ", len(filteredFiles))
- filteredFiles := idx.Filter(traversedFiles, 4)
- fmt.Println("filtered files:", filteredFiles)
+ idx.Documents = index.ParseDocs(filteredFiles, globalFlags.NumWorkers, indexFlags.ignoreBadDates)
+ fmt.Print(", Parsed ", len(idx.Documents), "\n")
- fmt.Println("Putting index")
- if err := query.Put(idx); err != nil {
- panic(err)
+ if err := querier.Put(idx); err != nil {
+ panic(err)
+ }
}
+
}