aboutsummaryrefslogtreecommitdiffstats
path: root/main.go
diff options
context:
space:
mode:
authorJP Appel <jeanpierre.appel01@gmail.com>2025-07-22 15:41:03 -0400
committerJP Appel <jeanpierre.appel01@gmail.com>2025-07-22 15:41:03 -0400
commit344c6526a8d6f490fc7628ddc7d2dd06ed1a07c1 (patch)
tree342878ff5d77b557533d6e5473e1d8f6e79ac6e9 /main.go
parentfaf35ef54885bc48b897508ce3cb40b868ff505b (diff)
Separate program entry point from commands
Diffstat (limited to 'main.go')
-rw-r--r--main.go182
1 files changed, 182 insertions, 0 deletions
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..e847a67
--- /dev/null
+++ b/main.go
@@ -0,0 +1,182 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io"
+ "log/slog"
+ "maps"
+ "os"
+ "slices"
+ "strings"
+
+ "github.com/jpappel/atlas/cmd"
+ "github.com/jpappel/atlas/pkg/data"
+ "github.com/jpappel/atlas/pkg/query"
+ "github.com/jpappel/atlas/pkg/shell"
+ "github.com/jpappel/atlas/pkg/util"
+)
+
+const VERSION = "0.4.1"
+const ExitCommand = 2 // exit because of a command parsing error
+
+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 main() {
+ globalFlags := cmd.GlobalFlags{}
+ cmd.SetupGlobalFlags(flag.CommandLine, &globalFlags)
+
+ indexFs := flag.NewFlagSet("index", flag.ExitOnError)
+ queryFs := flag.NewFlagSet("query", flag.ExitOnError)
+ shellFs := flag.NewFlagSet("debug", flag.ExitOnError)
+ serverFs := flag.NewFlagSet("server", flag.ExitOnError)
+ completionsFs := flag.NewFlagSet("completions", flag.ContinueOnError)
+
+ // set default usage for flagsets without subcommands
+ shellFs.Usage = addGlobalFlagUsage(shellFs)
+ serverFs.Usage = addGlobalFlagUsage(serverFs)
+
+ flag.Parse()
+ args := flag.Args()
+
+ queryFlags := cmd.QueryFlags{Outputer: query.DefaultOutput{}}
+ indexFlags := cmd.IndexFlags{}
+ serverFlags := cmd.ServerFlags{Port: 8080}
+
+ if len(args) < 1 {
+ fmt.Fprintln(os.Stderr, "No Command provided")
+ cmd.PrintHelp()
+ fmt.Fprintln(flag.CommandLine.Output(), "\nGlobal Flags:")
+ flag.PrintDefaults()
+ os.Exit(ExitCommand)
+ }
+ command := args[0]
+
+ switch command {
+ case "query", "q":
+ cmd.SetupQueryFlags(args[1:], queryFs, &queryFlags, globalFlags.DateFormat)
+ case "index", "i":
+ cmd.SetupIndexFlags(args[1:], indexFs, &indexFlags)
+ case "server":
+ cmd.SetupServerFlags(args[1:], serverFs, &serverFlags)
+ case "completions":
+ completionsFs.Parse(args[1:])
+ case "help":
+ cmd.PrintHelp()
+ flag.PrintDefaults()
+ return
+ case "shell":
+ shellFs.Parse(args[1:])
+ default:
+ fmt.Fprintln(os.Stderr, "Unrecognized command: ", command)
+ suggestedCommand, ok := util.Nearest(
+ command,
+ slices.Collect(maps.Keys(cmd.CommandHelp)),
+ util.LevensteinDistance, 3,
+ )
+ if ok {
+ fmt.Fprintf(os.Stderr, "Did you mean %s?\n\n", suggestedCommand)
+ }
+ cmd.PrintHelp()
+ os.Exit(ExitCommand)
+ }
+
+ slogLevel := &slog.LevelVar{}
+ loggerOpts := &slog.HandlerOptions{Level: slogLevel}
+ switch globalFlags.LogLevel {
+ case "debug":
+ slogLevel.Set(slog.LevelDebug)
+ loggerOpts.AddSource = true
+ case "info":
+ slogLevel.Set(slog.LevelInfo)
+ case "warn":
+ slogLevel.Set(slog.LevelWarn)
+ case "error":
+ slogLevel.Set(slog.LevelError)
+ default:
+ fmt.Fprintln(os.Stderr, "Unrecognized log level:", globalFlags.LogLevel)
+ os.Exit(ExitCommand)
+ }
+
+ var logFile *os.File
+ var err error
+ switch globalFlags.LogFile {
+ case "":
+ logFile = os.Stderr
+ case "-":
+ logFile = os.Stdout
+ default:
+ logFile, err = os.Create(globalFlags.LogFile)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot use log file `%s`: %s", globalFlags.LogFile, err)
+ os.Exit(1)
+ }
+ defer logFile.Close()
+ }
+
+ var logHandler slog.Handler
+ if globalFlags.LogJson {
+ logHandler = slog.NewJSONHandler(logFile, loggerOpts)
+ } else {
+ // strip time
+ loggerOpts.ReplaceAttr = func(groups []string, a slog.Attr) slog.Attr {
+ if a.Key == slog.TimeKey && len(groups) == 0 {
+ return slog.Attr{}
+ }
+ return a
+ }
+ logHandler = slog.NewTextHandler(logFile, loggerOpts)
+ }
+ logger := slog.New(logHandler)
+ slog.SetDefault(logger)
+
+ querier := data.NewQuery(globalFlags.DBPath)
+
+ // command specific
+ var exitCode int
+ switch command {
+ case "query", "q":
+ searchQuery := strings.Join(queryFs.Args(), " ")
+ exitCode = int(cmd.RunQuery(globalFlags, queryFlags, querier, searchQuery))
+ case "index", "i":
+ exitCode = int(cmd.RunIndex(globalFlags, indexFlags, querier))
+ case "server":
+ exitCode = int(cmd.RunServer(serverFlags, querier))
+ case "completions":
+ lang := completionsFs.Arg(0)
+ switch lang {
+ case "zsh":
+ cmd.ZshCompletions()
+ default:
+ fmt.Fprintf(os.Stderr, "Unrecognized completion language `%s`\n", lang)
+ fmt.Fprintf(os.Stderr, "Usage %s completions <language>\n", os.Args[0])
+ fmt.Fprintln(os.Stderr, "Supported languages: zsh")
+ exitCode = 2
+ }
+ case "shell":
+ state := make(shell.State)
+ env := make(map[string]string)
+
+ env["workers"] = fmt.Sprint(globalFlags.NumWorkers)
+ env["db_path"] = globalFlags.DBPath
+ env["index_root"] = globalFlags.IndexRoot
+ env["version"] = VERSION
+
+ interpreter := shell.NewInterpreter(state, env, globalFlags.NumWorkers, querier)
+ if err := interpreter.Run(); err != nil && err != io.EOF {
+ slog.Error("Fatal error occured", slog.String("err", err.Error()))
+ exitCode = 1
+ }
+ }
+
+ querier.Close()
+ os.Exit(exitCode)
+}