@@ -6,6 +6,8 @@ package internal
66
77import (
88 "context"
9+ "errors"
10+ "flag"
911 "fmt"
1012 "os"
1113 "os/signal"
@@ -15,7 +17,6 @@ import (
1517 "github.com/hugoh/upd/internal/logger"
1618 "github.com/hugoh/upd/internal/logic"
1719 "github.com/hugoh/upd/internal/version"
18- "github.com/urfave/cli/v3"
1920)
2021
2122const (
@@ -36,6 +37,12 @@ const (
3637 ConfigDebug string = "debug"
3738)
3839
40+ // Flags holds the parsed command-line flags.
41+ type Flags struct {
42+ ConfigPath string
43+ Debug bool
44+ }
45+
3946// SetupLoop initializes the loop with configuration from the given file.
4047func SetupLoop (loop * logic.Loop , configPath string ) (* config.Configuration , error ) {
4148 newConf , err := config .ReadConf (configPath )
@@ -60,8 +67,8 @@ func SetupLoop(loop *logic.Loop, configPath string) (*config.Configuration, erro
6067}
6168
6269// Run is the main application entry point handling signals and configuration reload.
63- func Run (appCtx context.Context , cmd * cli. Command ) error {
64- logger .LogSetup (cmd . Bool ( ConfigDebug ) )
70+ func Run (appCtx context.Context , flags Flags ) error {
71+ logger .LogSetup (flags . Debug )
6572
6673 rootCtx , stopSignalHandlers := signal .NotifyContext (appCtx , syscall .SIGINT , syscall .SIGTERM )
6774 defer stopSignalHandlers ()
@@ -88,7 +95,7 @@ func Run(appCtx context.Context, cmd *cli.Command) error {
8895 go func (ctx context.Context ) {
8996 defer close (done )
9097
91- newConf , err := SetupLoop (loop , cmd . String ( ConfigConfig ) )
98+ newConf , err := SetupLoop (loop , flags . ConfigPath )
9299 if err != nil {
93100 errCh <- fmt .Errorf ("cannot configure app: %w" , err )
94101
@@ -157,34 +164,70 @@ func waitForWorker(
157164 }
158165}
159166
160- // Cmd creates and runs the CLI application.
161- func Cmd () error {
162- flags := []cli.Flag {
163- & cli.StringFlag {
164- Name : ConfigConfig ,
165- Aliases : []string {"c" },
166- Usage : "use the specified TOML configuration file" ,
167- Value : config .DefaultConfig ,
168- TakesFile : true ,
169- },
170- & cli.BoolFlag {
171- Name : ConfigDebug ,
172- Aliases : []string {"d" },
173- Value : false ,
174- Usage : "display debugging output in the console" ,
175- },
167+ // ParseFlags parses the given command-line arguments (excluding the program
168+ // name) into Flags. It returns flag.ErrHelp when --help or --version was
169+ // requested and already handled, in which case the caller should exit
170+ // without running the app.
171+ func ParseFlags (args []string ) (Flags , error ) {
172+ var (
173+ flags Flags
174+ showVersion bool
175+ )
176+
177+ flagSet := flag .NewFlagSet (AppName , flag .ContinueOnError )
178+ flagSet .Usage = func () {
179+ usage := fmt .Sprintf (
180+ "%s - %s\n \n Usage:\n %s [flags]\n \n Flags:\n " ,
181+ AppName ,
182+ AppShort ,
183+ AppName ,
184+ )
185+ if _ , err := fmt .Fprint (flagSet .Output (), usage ); err != nil {
186+ return
187+ }
188+
189+ flagSet .PrintDefaults ()
190+ }
191+
192+ flagSet .StringVar (
193+ & flags .ConfigPath ,
194+ ConfigConfig ,
195+ config .DefaultConfig ,
196+ "use the specified TOML configuration file" ,
197+ )
198+ flagSet .StringVar (& flags .ConfigPath , "c" , config .DefaultConfig , "shorthand for --" + ConfigConfig )
199+ flagSet .BoolVar (& flags .Debug , ConfigDebug , false , "display debugging output in the console" )
200+ flagSet .BoolVar (& flags .Debug , "d" , false , "shorthand for --" + ConfigDebug )
201+ flagSet .BoolVar (& showVersion , "version" , false , "print the version and exit" )
202+
203+ if err := flagSet .Parse (args ); err != nil {
204+ return Flags {}, fmt .Errorf ("failed to parse flags: %w" , err )
176205 }
177206
178- app := & cli.Command {
179- Name : AppName ,
180- Usage : AppShort ,
181- Version : version .Version (),
182- Flags : flags ,
183- Action : Run ,
207+ if showVersion {
208+ versionLine := fmt .Sprintf ("%s version %s\n " , AppName , version .Version ())
209+ if _ , err := fmt .Fprint (os .Stdout , versionLine ); err != nil {
210+ return Flags {}, fmt .Errorf ("failed to print version: %w" , err )
211+ }
212+
213+ return Flags {}, flag .ErrHelp
184214 }
185215
186- err := app .Run (context .Background (), os .Args )
216+ return flags , nil
217+ }
218+
219+ // Cmd creates and runs the CLI application.
220+ func Cmd () error {
221+ flags , err := ParseFlags (os .Args [1 :])
187222 if err != nil {
223+ if errors .Is (err , flag .ErrHelp ) {
224+ return nil
225+ }
226+
227+ return err
228+ }
229+
230+ if err := Run (context .Background (), flags ); err != nil {
188231 return fmt .Errorf ("failed to run app: %w" , err )
189232 }
190233
0 commit comments