diff --git a/src/cmd/attach.go b/src/cmd/attach.go index 1a45960..271ea55 100644 --- a/src/cmd/attach.go +++ b/src/cmd/attach.go @@ -15,7 +15,7 @@ var attachCmd = &cobra.Command{ func init() { rootCmd.AddCommand(attachCmd) - attachCmd.Flags().IntVarP(pcFlags.RefreshRate, "ref-rate", "r", *pcFlags.RefreshRate, "TUI refresh rate in seconds") + attachCmd.Flags().VarP(refreshRateFlag{pcFlags.RefreshRate}, "ref-rate", "r", "TUI refresh rate in seconds or as a Go duration string (e.g. 1s)") attachCmd.Flags().StringVarP(pcFlags.Address, "address", "a", *pcFlags.Address, "address of the target process compose server") attachCmd.Flags().IntVarP(pcFlags.LogLength, "log-length", "l", *pcFlags.LogLength, "log length to display in TUI") attachCmd.Flags().AddFlag(commonFlags.Lookup(flagReverse)) diff --git a/src/cmd/project_runner.go b/src/cmd/project_runner.go index bdb7a5e..6390a62 100644 --- a/src/cmd/project_runner.go +++ b/src/cmd/project_runner.go @@ -10,7 +10,6 @@ import ( "os" "os/signal" "syscall" - "time" ) func getProjectRunner(process []string, noDeps bool, mainProcess string, mainProcessArgs []string) *app.ProjectRunner { @@ -82,7 +81,7 @@ func runTui(project *app.ProjectRunner) int { func startTui(runner app.IProject) { tuiOptions := []tui.Option{ - tui.WithRefreshRate(time.Duration(*pcFlags.RefreshRate) * time.Second), + tui.WithRefreshRate(*pcFlags.RefreshRate), } if !*pcFlags.IsReadOnlyMode { config.CreateProcCompHome() diff --git a/src/cmd/root.go b/src/cmd/root.go index 346ff39..07b0561 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -15,6 +15,7 @@ import ( "os" "path" "runtime" + "strconv" "time" ) @@ -72,7 +73,7 @@ func init() { rootCmd.PersistentFlags().BoolVar(pcFlags.NoServer, "no-server", *pcFlags.NoServer, "disable HTTP server (env: "+config.EnvVarNameNoServer+")") rootCmd.PersistentFlags().BoolVar(pcFlags.IsOrderedShutDown, "ordered-shutdown", *pcFlags.IsOrderedShutDown, "shut down processes in reverse dependency order") rootCmd.Flags().BoolVarP(pcFlags.HideDisabled, "hide-disabled", "d", *pcFlags.HideDisabled, "hide disabled processes") - rootCmd.Flags().IntVarP(pcFlags.RefreshRate, "ref-rate", "r", *pcFlags.RefreshRate, "TUI refresh rate in seconds") + rootCmd.Flags().VarP(refreshRateFlag{pcFlags.RefreshRate}, "ref-rate", "r", "TUI refresh rate in seconds or as a Go duration string (e.g. 1s)") rootCmd.PersistentFlags().IntVarP(pcFlags.PortNum, "port", "p", *pcFlags.PortNum, "port number (env: "+config.EnvVarNamePort+")") rootCmd.Flags().StringArrayVarP(&opts.FileNames, "config", "f", config.GetConfigDefault(), "path to config files to load (env: "+config.EnvVarNameConfig+")") rootCmd.Flags().StringArrayVarP(&nsAdmitter.EnabledNamespaces, "namespace", "n", nil, "run only specified namespaces (default all)") @@ -159,3 +160,37 @@ func isUnixSocketMode(cmd *cobra.Command) bool { os.Getenv(config.EnvVarUnixSocketPath) != "" || cmd.Flags().Changed("unix-socket") } + +// refreshRateFlag is a custom flag type for the TUI's refresh rate. +// It accepts both an integer in seconds and a duration string. +type refreshRateFlag struct { + dst *time.Duration +} + +func (f refreshRateFlag) String() string { + d := *f.dst + if d%time.Second == 0 { + return strconv.Itoa(int(d / time.Second)) + } + return d.String() +} + +func (f refreshRateFlag) Set(str string) error { + i, err := strconv.Atoi(str) + if err == nil { + *f.dst = time.Duration(i) * time.Second + return nil + } + d, err := time.ParseDuration(str) + if err == nil { + *f.dst = d + return nil + } + return fmt.Errorf( + "invalid refresh rate %q, must be a duration or an integer in seconds", + str) +} + +func (f refreshRateFlag) Type() string { + return "duration" +} diff --git a/src/config/Flags.go b/src/config/Flags.go index 7ca0d0e..3d38174 100644 --- a/src/config/Flags.go +++ b/src/config/Flags.go @@ -2,11 +2,12 @@ package config import ( "math" + "time" ) const ( // DefaultRefreshRate represents the refresh interval. - DefaultRefreshRate = 1 // secs + DefaultRefreshRate = 1 * time.Second // DefaultLogLevel represents the default log level. DefaultLogLevel = "info" @@ -38,7 +39,7 @@ const ( // Flags represents PC configuration flags. type Flags struct { - RefreshRate *int + RefreshRate *time.Duration PortNum *int Address *string LogLevel *string diff --git a/src/tui/proc-table.go b/src/tui/proc-table.go index 4b07956..f01d22a 100644 --- a/src/tui/proc-table.go +++ b/src/tui/proc-table.go @@ -195,12 +195,14 @@ func (pv *pcView) updateTable(ctx context.Context) { pv.appView.QueueUpdateDraw(func() { pv.fillTableData() }) + ticker := time.NewTicker(pv.refreshRate) + defer ticker.Stop() for { select { case <-ctx.Done(): log.Debug().Msg("Table monitoring canceled") return - case <-time.After(pv.refreshRate): + case <-ticker.C: pv.appView.QueueUpdateDraw(func() { pv.fillTableData() })