From 555c8cd918b1253723464604d6871185991e5edc Mon Sep 17 00:00:00 2001 From: Jeff Carr Date: Fri, 17 Oct 2025 05:36:18 -0500 Subject: rename files to help organize my thoughts --- argv.Exit.go | 75 +++++++++++++++++++++++++ argv.LoadHistory.go | 28 ++++++++++ argv.LoadSafe.go | 28 ---------- argv.SendStrings.go | 70 ++++++++++++++++++++++++ exit.go | 75 ------------------------- parseArgv.go | 155 ---------------------------------------------------- parseOsArgs.go | 155 ++++++++++++++++++++++++++++++++++++++++++++++++++++ sendStrings.go | 70 ------------------------ 8 files changed, 328 insertions(+), 328 deletions(-) create mode 100644 argv.Exit.go create mode 100644 argv.LoadHistory.go delete mode 100644 argv.LoadSafe.go create mode 100644 argv.SendStrings.go delete mode 100644 exit.go delete mode 100644 parseArgv.go create mode 100644 parseOsArgs.go delete mode 100644 sendStrings.go diff --git a/argv.Exit.go b/argv.Exit.go new file mode 100644 index 0000000..1419ae2 --- /dev/null +++ b/argv.Exit.go @@ -0,0 +1,75 @@ +package argvpb + +import ( + "fmt" + "os" + "time" + + "go.wit.com/lib/cobol" + "go.wit.com/log" +) + +// since we know when the command starts (duh, this parses os.Args) +// this is a convienent way to provide a standard exit format back +// to the shell that also has built in timing! + +// also, it supports a custom Exit() back to your application + +func (pb *Argv) GoodExit(msg string) { + go ExitWatchdog() + if me.appExit != nil { + me.appExit() + } + dur := time.Since(pb.Ctime.AsTime()) + log.Infof("%s: %s (%s)\n", pb.Argname, msg, cobol.FormatDuration(dur)) + os.Exit(0) +} + +func (pb *Argv) BadExit(msg string, err error) { + go ExitWatchdog() + if me.appExit != nil { + me.appExit() + } + // print out errors. this handles wrapped errors which is a useful + if err != nil { + if u, ok := err.(interface{ Unwrap() []error }); ok { + // If it does, call the method to get the slice of errors. + allerr := u.Unwrap() + for _, e := range allerr { + log.Info("Error:", e) + } + } else { + // If it's not a joined error, you can fall back to the single-unwrap loop. + log.Info("Error:", err) + } + } + + dur := time.Since(pb.Ctime.AsTime()) + log.Infof("%s error: %s (%s)\n", pb.Argname, msg, cobol.FormatDuration(dur)) + os.Exit(1) +} + +// this code doesn't need to be this complicated. I put it here as reference code for myself so I could remember where it is. +func ExitWatchdog() { + dog := time.NewTicker(5 * time.Second) + defer dog.Stop() + dogchan := make(chan bool) + /* + // this example would exit/destroy the ticker in 10 seconds + go func() { + time.Sleep(10 * time.Second) + done <- true + }() + */ + for { + select { + case <-dogchan: + fmt.Println("Done!") + return + case t := <-dog.C: + _ = t + log.Info("argv.Exit() watchdog: stalled in", me.ARGNAME+".Exit()") + // h.Scan() + } + } +} diff --git a/argv.LoadHistory.go b/argv.LoadHistory.go new file mode 100644 index 0000000..a66758c --- /dev/null +++ b/argv.LoadHistory.go @@ -0,0 +1,28 @@ +// Code generated by go.wit.com/apps/autogenpb DO NOT EDIT. +// go install go.wit.com/apps/autogenpb@latest +// +// This file was autogenerated with autogenpb: +// autogenpb v0.5.27 Built on 2025/10/16 17:13:05 ( 50 m) +// Theese sort.pb.go and marshal.pb.go files are autogenerated +// The autogenpb sources have example .proto files with instructions +// + +package argvpb + +import ( + "errors" + + "go.wit.com/lib/config" +) + +// deletes and remakes the PB file when .proto versions change +func (pb *Argvs) LoadSafe() error { + err := config.LoadPB(pb) + if errors.Is(err, config.VersionMismatch) { + panic("fixme in autopb") + // TODO: erase and recreate the PB file + } else if err != nil { + panic("still fixme in") + } + return err +} diff --git a/argv.LoadSafe.go b/argv.LoadSafe.go deleted file mode 100644 index a66758c..0000000 --- a/argv.LoadSafe.go +++ /dev/null @@ -1,28 +0,0 @@ -// Code generated by go.wit.com/apps/autogenpb DO NOT EDIT. -// go install go.wit.com/apps/autogenpb@latest -// -// This file was autogenerated with autogenpb: -// autogenpb v0.5.27 Built on 2025/10/16 17:13:05 ( 50 m) -// Theese sort.pb.go and marshal.pb.go files are autogenerated -// The autogenpb sources have example .proto files with instructions -// - -package argvpb - -import ( - "errors" - - "go.wit.com/lib/config" -) - -// deletes and remakes the PB file when .proto versions change -func (pb *Argvs) LoadSafe() error { - err := config.LoadPB(pb) - if errors.Is(err, config.VersionMismatch) { - panic("fixme in autopb") - // TODO: erase and recreate the PB file - } else if err != nil { - panic("still fixme in") - } - return err -} diff --git a/argv.SendStrings.go b/argv.SendStrings.go new file mode 100644 index 0000000..827e690 --- /dev/null +++ b/argv.SendStrings.go @@ -0,0 +1,70 @@ +package argvpb + +// sends the autocomplete strings to the shell +// also where custom strings are pulled in from the application +// calls into go-args for strings parsed by MustParse() + +import ( + "fmt" + "os" + "strings" + + "go.wit.com/log" +) + +// the application must send a string "help run list" +func (pb *Argv) SendString(sendthis string) { + pb.SendStrings(strings.Split(sendthis, " ")) +} + +// the application must send an array []string{"help", "run", "list"} +func (pb *Argv) SendStrings(parts []string) { + // parts := strings.Split(sendthis, " ") + var all []string + for _, part := range parts { + var found bool + for _, s := range os.Args { + if s == part { + found = true + } + } + if found { + continue + } + all = append(all, part) + } + fmt.Printf("%s", strings.Join(all, " ")) +} + +// try out a new name. also, this whole thing is dumb and needs to be redone +func (pb *Argv) GenerateSubCommandStrings(cmd ...string) { + pb.SubCommand(cmd...) +} + +func (pb *Argv) SubCommand(cmd ...string) { + partial := strings.Trim(pb.Partial, "'") + if pb.Debug { + if me.examples == nil { + pb.Debugf("WRITE DEBUG: argv.Examples() not defined") + // log.Fprintf(os.Stderr, "\n") + // log.Fprintf(os.Stderr, "examples was nil\n") + // log.Fprintf(os.Stderr, "\n") + } else { + log.Fprintf(os.Stderr, "\n") + log.Fprintf(os.Stderr, "\n") + log.Fprintf(os.Stderr, "Examples:\n") + for _, line := range strings.Split(me.examples(), "\n") { + log.Fprintf(os.Stderr, " %s\n", line) + } + // log.Fprintf(os.Stderr, "\n") + } + me.pp.WriteHelpForAutocomplete(os.Stderr, os.Stdout, partial, cmd...) + // me.pp.GetUsageForSubcommand(os.Stdout, os.Stderr, partial, cmd) + // me.pp.GetUsageForSubcommand(os.Stdout, nil, partial, cmd) + } else { + f, _ := os.OpenFile("/tmp/outlook", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + me.pp.WriteHelpForAutocomplete(f, os.Stdout, partial, cmd...) + // me.pp.GetUsageForSubcommand(os.Stdout, nil, partial, cmd) + } + os.Exit(0) +} diff --git a/exit.go b/exit.go deleted file mode 100644 index 1419ae2..0000000 --- a/exit.go +++ /dev/null @@ -1,75 +0,0 @@ -package argvpb - -import ( - "fmt" - "os" - "time" - - "go.wit.com/lib/cobol" - "go.wit.com/log" -) - -// since we know when the command starts (duh, this parses os.Args) -// this is a convienent way to provide a standard exit format back -// to the shell that also has built in timing! - -// also, it supports a custom Exit() back to your application - -func (pb *Argv) GoodExit(msg string) { - go ExitWatchdog() - if me.appExit != nil { - me.appExit() - } - dur := time.Since(pb.Ctime.AsTime()) - log.Infof("%s: %s (%s)\n", pb.Argname, msg, cobol.FormatDuration(dur)) - os.Exit(0) -} - -func (pb *Argv) BadExit(msg string, err error) { - go ExitWatchdog() - if me.appExit != nil { - me.appExit() - } - // print out errors. this handles wrapped errors which is a useful - if err != nil { - if u, ok := err.(interface{ Unwrap() []error }); ok { - // If it does, call the method to get the slice of errors. - allerr := u.Unwrap() - for _, e := range allerr { - log.Info("Error:", e) - } - } else { - // If it's not a joined error, you can fall back to the single-unwrap loop. - log.Info("Error:", err) - } - } - - dur := time.Since(pb.Ctime.AsTime()) - log.Infof("%s error: %s (%s)\n", pb.Argname, msg, cobol.FormatDuration(dur)) - os.Exit(1) -} - -// this code doesn't need to be this complicated. I put it here as reference code for myself so I could remember where it is. -func ExitWatchdog() { - dog := time.NewTicker(5 * time.Second) - defer dog.Stop() - dogchan := make(chan bool) - /* - // this example would exit/destroy the ticker in 10 seconds - go func() { - time.Sleep(10 * time.Second) - done <- true - }() - */ - for { - select { - case <-dogchan: - fmt.Println("Done!") - return - case t := <-dog.C: - _ = t - log.Info("argv.Exit() watchdog: stalled in", me.ARGNAME+".Exit()") - // h.Scan() - } - } -} diff --git a/parseArgv.go b/parseArgv.go deleted file mode 100644 index c91c1e5..0000000 --- a/parseArgv.go +++ /dev/null @@ -1,155 +0,0 @@ -package argvpb - -// initializes logging and command line options - -import ( - "fmt" - "os" - "strings" - - "go.wit.com/log" -) - -func parseArgv(argname string) *Argv { - pb := new(Argv) - pb.Argname = argname - - // this shouldn't really happen. OS/POSIX error? - if len(os.Args) == 0 { - // fmt.Fprintf(os.Stderr, "what OS is this?\n") - return pb - } - - // there is nothing on the command line. user just ran "forge" and hit enter - if len(os.Args) == 1 { - pb.Arg0 = os.Args[0] - return pb - } - - // try to setup bash autocomplete and exit - if len(os.Args) > 1 && os.Args[1] == "--bash" { - pb.SetupAuto = true - return pb - } - - // try to setup zsh autocomplete and exit - if len(os.Args) > 1 && os.Args[1] == "--zsh" { - pb.SetupAuto = true - return pb - } - - // set debug flag if --argvdebug is passed - for _, s := range os.Args { - if s == "--argvdebug" { - pb.Debug = true - } - // deprecate - if s == "--autodebug" { - pb.Debug = true - } - } - - // wtf is this. I've forgotten. todo: figure this out - if len(os.Args) > 1 && os.Args[1] == pb.Argname { - pb.IsAuto = true - parts := strings.Split(os.Getenv("COMP_LINE"), " ") - pb.Debug = true - log.Fprintf(os.Stderr, "\n") - pb.Debugf("MATCH Partial os.Args=%v COMP_LINE=%v", os.Args, os.Getenv("COMP_LINE")) - // log.Fprintf(os.Stdout, "jcarr") - if len(parts) > 0 { - pb.Arg0 = parts[0] - } - pb.Arg1 = os.Args[1] - os.Exit(0) - } - - // print the version and exit - if len(os.Args) > 1 && os.Args[1] == "--version" { - if me.buildtime != nil { - // if binary defined buildtime() then process standard version output here - doVersion(pb) - os.Exit(0) - } - } - - if os.Args[1] != "--auto-complete" { - // if the first arg is not --auto-complete, then don't go any farther - for _, s := range os.Args[1:] { - if strings.HasPrefix(s, "-") { - // option is something like --verbose - // skip these. they are not subcommands - continue - } - // found the subcommand - pb.Cmd = s - break - } - // exit here. not autocomplete - // todo: actually finish parsing the pb. is it safe to continue from here? - return pb - } - - // should we do auto complete here? - if len(os.Args) > 1 && os.Args[1] == "--auto-complete" { - pb.IsAuto = true - pb.Arg0 = os.Args[0] - pb.Arg1 = os.Args[1] - pb.Partial = os.Args[2] - pb.Arg3 = os.Args[3] - if len(os.Args) < 5 { - // the user is doing autocomplete on the command itself - pb.Partial = "" - pb.Cmd = "" - pb.Real = []string{""} - return pb - } - if pb.Partial == "''" { - pb.Partial = "" - } - // pb.Argv = os.Args[4:] - for _, s := range os.Args[4:] { - if s == "--autodebug" { - continue - } - tmp := strings.Trim(pb.Partial, "'") - if tmp == s { - // don't put pb.Partial into Argv - continue - } - pb.Real = append(pb.Real, s) - } - // set pb.Cmd to the first thing that doesn't have a '-' arg - for _, s := range pb.Real { - if strings.HasPrefix(s, "-") { - continue - } - pb.Cmd = s - break - } - if pb.Partial == "'"+pb.Cmd+"'" { - // not really a command, it's just a partially inputed string from the user - pb.Cmd = "" - } - // try to figure out what the last argv is - for _, s := range pb.Real { - p := fmt.Sprintf("'%s'", s) - if pb.Partial == p { - pb.Debugf("DEBUG: Last argv MATCHES Partial %s %s", s, p) - continue - } else { - pb.Debugf("DEBUG: Last argv DOES NOT MATCH Partial %s %s", s, p) - } - pb.Last = s - if strings.HasPrefix(s, "-") { - // skip args like -test --verbose when sending subcommands to go-args for help text - continue - } - pb.Goargs = append(pb.Goargs, s) - } - // if pb.Cmd == "" { - // pb.Cmd = strings.Join(pb.Argv, "BLAH") - // } - } - return pb -} diff --git a/parseOsArgs.go b/parseOsArgs.go new file mode 100644 index 0000000..c91c1e5 --- /dev/null +++ b/parseOsArgs.go @@ -0,0 +1,155 @@ +package argvpb + +// initializes logging and command line options + +import ( + "fmt" + "os" + "strings" + + "go.wit.com/log" +) + +func parseArgv(argname string) *Argv { + pb := new(Argv) + pb.Argname = argname + + // this shouldn't really happen. OS/POSIX error? + if len(os.Args) == 0 { + // fmt.Fprintf(os.Stderr, "what OS is this?\n") + return pb + } + + // there is nothing on the command line. user just ran "forge" and hit enter + if len(os.Args) == 1 { + pb.Arg0 = os.Args[0] + return pb + } + + // try to setup bash autocomplete and exit + if len(os.Args) > 1 && os.Args[1] == "--bash" { + pb.SetupAuto = true + return pb + } + + // try to setup zsh autocomplete and exit + if len(os.Args) > 1 && os.Args[1] == "--zsh" { + pb.SetupAuto = true + return pb + } + + // set debug flag if --argvdebug is passed + for _, s := range os.Args { + if s == "--argvdebug" { + pb.Debug = true + } + // deprecate + if s == "--autodebug" { + pb.Debug = true + } + } + + // wtf is this. I've forgotten. todo: figure this out + if len(os.Args) > 1 && os.Args[1] == pb.Argname { + pb.IsAuto = true + parts := strings.Split(os.Getenv("COMP_LINE"), " ") + pb.Debug = true + log.Fprintf(os.Stderr, "\n") + pb.Debugf("MATCH Partial os.Args=%v COMP_LINE=%v", os.Args, os.Getenv("COMP_LINE")) + // log.Fprintf(os.Stdout, "jcarr") + if len(parts) > 0 { + pb.Arg0 = parts[0] + } + pb.Arg1 = os.Args[1] + os.Exit(0) + } + + // print the version and exit + if len(os.Args) > 1 && os.Args[1] == "--version" { + if me.buildtime != nil { + // if binary defined buildtime() then process standard version output here + doVersion(pb) + os.Exit(0) + } + } + + if os.Args[1] != "--auto-complete" { + // if the first arg is not --auto-complete, then don't go any farther + for _, s := range os.Args[1:] { + if strings.HasPrefix(s, "-") { + // option is something like --verbose + // skip these. they are not subcommands + continue + } + // found the subcommand + pb.Cmd = s + break + } + // exit here. not autocomplete + // todo: actually finish parsing the pb. is it safe to continue from here? + return pb + } + + // should we do auto complete here? + if len(os.Args) > 1 && os.Args[1] == "--auto-complete" { + pb.IsAuto = true + pb.Arg0 = os.Args[0] + pb.Arg1 = os.Args[1] + pb.Partial = os.Args[2] + pb.Arg3 = os.Args[3] + if len(os.Args) < 5 { + // the user is doing autocomplete on the command itself + pb.Partial = "" + pb.Cmd = "" + pb.Real = []string{""} + return pb + } + if pb.Partial == "''" { + pb.Partial = "" + } + // pb.Argv = os.Args[4:] + for _, s := range os.Args[4:] { + if s == "--autodebug" { + continue + } + tmp := strings.Trim(pb.Partial, "'") + if tmp == s { + // don't put pb.Partial into Argv + continue + } + pb.Real = append(pb.Real, s) + } + // set pb.Cmd to the first thing that doesn't have a '-' arg + for _, s := range pb.Real { + if strings.HasPrefix(s, "-") { + continue + } + pb.Cmd = s + break + } + if pb.Partial == "'"+pb.Cmd+"'" { + // not really a command, it's just a partially inputed string from the user + pb.Cmd = "" + } + // try to figure out what the last argv is + for _, s := range pb.Real { + p := fmt.Sprintf("'%s'", s) + if pb.Partial == p { + pb.Debugf("DEBUG: Last argv MATCHES Partial %s %s", s, p) + continue + } else { + pb.Debugf("DEBUG: Last argv DOES NOT MATCH Partial %s %s", s, p) + } + pb.Last = s + if strings.HasPrefix(s, "-") { + // skip args like -test --verbose when sending subcommands to go-args for help text + continue + } + pb.Goargs = append(pb.Goargs, s) + } + // if pb.Cmd == "" { + // pb.Cmd = strings.Join(pb.Argv, "BLAH") + // } + } + return pb +} diff --git a/sendStrings.go b/sendStrings.go deleted file mode 100644 index 827e690..0000000 --- a/sendStrings.go +++ /dev/null @@ -1,70 +0,0 @@ -package argvpb - -// sends the autocomplete strings to the shell -// also where custom strings are pulled in from the application -// calls into go-args for strings parsed by MustParse() - -import ( - "fmt" - "os" - "strings" - - "go.wit.com/log" -) - -// the application must send a string "help run list" -func (pb *Argv) SendString(sendthis string) { - pb.SendStrings(strings.Split(sendthis, " ")) -} - -// the application must send an array []string{"help", "run", "list"} -func (pb *Argv) SendStrings(parts []string) { - // parts := strings.Split(sendthis, " ") - var all []string - for _, part := range parts { - var found bool - for _, s := range os.Args { - if s == part { - found = true - } - } - if found { - continue - } - all = append(all, part) - } - fmt.Printf("%s", strings.Join(all, " ")) -} - -// try out a new name. also, this whole thing is dumb and needs to be redone -func (pb *Argv) GenerateSubCommandStrings(cmd ...string) { - pb.SubCommand(cmd...) -} - -func (pb *Argv) SubCommand(cmd ...string) { - partial := strings.Trim(pb.Partial, "'") - if pb.Debug { - if me.examples == nil { - pb.Debugf("WRITE DEBUG: argv.Examples() not defined") - // log.Fprintf(os.Stderr, "\n") - // log.Fprintf(os.Stderr, "examples was nil\n") - // log.Fprintf(os.Stderr, "\n") - } else { - log.Fprintf(os.Stderr, "\n") - log.Fprintf(os.Stderr, "\n") - log.Fprintf(os.Stderr, "Examples:\n") - for _, line := range strings.Split(me.examples(), "\n") { - log.Fprintf(os.Stderr, " %s\n", line) - } - // log.Fprintf(os.Stderr, "\n") - } - me.pp.WriteHelpForAutocomplete(os.Stderr, os.Stdout, partial, cmd...) - // me.pp.GetUsageForSubcommand(os.Stdout, os.Stderr, partial, cmd) - // me.pp.GetUsageForSubcommand(os.Stdout, nil, partial, cmd) - } else { - f, _ := os.OpenFile("/tmp/outlook", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - me.pp.WriteHelpForAutocomplete(f, os.Stdout, partial, cmd...) - // me.pp.GetUsageForSubcommand(os.Stdout, nil, partial, cmd) - } - os.Exit(0) -} -- cgit v1.2.3