From c868aa1c6ce85e1a3c62ba47ca96e838a8ac3e34 Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Mon, 18 Nov 2024 10:49:15 -0300 Subject: [PATCH] fix(confirm,choose,file,input): timeout handling (#718) * fix(confirm,choose,file,input): timeout handling - some fields were not actually using the `--timeout` value - some fields had different behavior when a timeout did occur. On this matter, it seems to me the best way forward is to specifically say it timed out, and after how long - added exit status 124 (copied from `timeout` from coreutils) (fixes #684) Signed-off-by: Carlos Alexandro Becker * Update main.go Co-authored-by: Ayman Bagabas * Update internal/exit/exit.go Co-authored-by: ccoVeille <3875889+ccoVeille@users.noreply.github.com> * fix: improve Signed-off-by: Carlos Alexandro Becker * fix: stderr --------- Signed-off-by: Carlos Alexandro Becker Co-authored-by: Ayman Bagabas Co-authored-by: ccoVeille <3875889+ccoVeille@users.noreply.github.com> --- choose/command.go | 11 +++++++---- confirm/command.go | 7 ++----- file/command.go | 5 +++-- input/command.go | 4 +++- internal/exit/exit.go | 23 ++++++++++++++++++++--- main.go | 8 ++++++-- 6 files changed, 41 insertions(+), 17 deletions(-) diff --git a/choose/command.go b/choose/command.go index f96b1beba..d4b1c4219 100644 --- a/choose/command.go +++ b/choose/command.go @@ -11,6 +11,7 @@ import ( "github.com/charmbracelet/x/ansi" "github.com/charmbracelet/x/term" + "github.com/charmbracelet/gum/internal/exit" "github.com/charmbracelet/gum/internal/stdin" ) @@ -76,9 +77,10 @@ func (o Options) Run() error { WithWidth(width). WithShowHelp(o.ShowHelp). WithTheme(theme). + WithTimeout(o.Timeout). Run() - if err != nil && !errors.Is(err, huh.ErrTimeout) { - return err + if err != nil { + return exit.Handle(err, o.Timeout) } if len(choices) > 0 { s := strings.Join(choices, "\n") @@ -100,10 +102,11 @@ func (o Options) Run() error { ). WithWidth(width). WithTheme(theme). + WithTimeout(o.Timeout). WithShowHelp(o.ShowHelp). Run() - if err != nil && !errors.Is(err, huh.ErrTimeout) { - return err + if err != nil { + return exit.Handle(err, o.Timeout) } if term.IsTerminal(os.Stdout.Fd()) { diff --git a/confirm/command.go b/confirm/command.go index 998510893..3653502d3 100644 --- a/confirm/command.go +++ b/confirm/command.go @@ -1,10 +1,9 @@ package confirm import ( - "errors" - "fmt" "os" + "github.com/charmbracelet/gum/internal/exit" "github.com/charmbracelet/huh" ) @@ -32,9 +31,7 @@ func (o Options) Run() error { WithShowHelp(o.ShowHelp). Run() if err != nil { - if o.Timeout > 0 && errors.Is(err, huh.ErrTimeout) { - return fmt.Errorf("unable to run confirm: %w", err) - } + return exit.Handle(err, o.Timeout) } if !choice { diff --git a/file/command.go b/file/command.go index 7d02b6e3c..9d8b50fff 100644 --- a/file/command.go +++ b/file/command.go @@ -5,6 +5,7 @@ import ( "fmt" "path/filepath" + "github.com/charmbracelet/gum/internal/exit" "github.com/charmbracelet/huh" "github.com/charmbracelet/lipgloss" ) @@ -51,14 +52,14 @@ func (o Options) Run() error { Value(&path), ), ). + WithTimeout(o.Timeout). WithShowHelp(o.ShowHelp). WithKeyMap(keymap). WithTheme(theme). Run() if err != nil { - return err + return exit.Handle(err, o.Timeout) } - fmt.Println(path) return nil } diff --git a/input/command.go b/input/command.go index b35ce2084..d35515eac 100644 --- a/input/command.go +++ b/input/command.go @@ -9,6 +9,7 @@ import ( "github.com/charmbracelet/huh" "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/gum/internal/exit" "github.com/charmbracelet/gum/internal/stdin" ) @@ -53,11 +54,12 @@ func (o Options) Run() error { WithWidth(o.Width). WithTheme(theme). WithKeyMap(keymap). + WithTimeout(o.Timeout). WithShowHelp(o.ShowHelp). WithProgramOptions(tea.WithOutput(os.Stderr)). Run() if err != nil { - return err + return exit.Handle(err, o.Timeout) } fmt.Println(value) diff --git a/internal/exit/exit.go b/internal/exit/exit.go index ff3a2b95c..e79d7c22f 100644 --- a/internal/exit/exit.go +++ b/internal/exit/exit.go @@ -1,9 +1,26 @@ package exit -import "fmt" +import ( + "errors" + "fmt" + "time" + + "github.com/charmbracelet/huh" +) + +// StatusTimeout is the exit code for timed out commands. +const StatusTimeout = 124 // StatusAborted is the exit code for aborted commands. const StatusAborted = 130 -// ErrAborted is the error to return when a gum command is aborted by Ctrl + C. -var ErrAborted = fmt.Errorf("aborted") +// ErrAborted is the error to return when a gum command is aborted by Ctrl+C. +var ErrAborted = huh.ErrUserAborted + +// Handle handles the error. +func Handle(err error, d time.Duration) error { + if errors.Is(err, huh.ErrTimeout) { + return fmt.Errorf("%w after %s", huh.ErrTimeout, d) + } + return err +} diff --git a/main.go b/main.go index b8f34076d..301baefde 100644 --- a/main.go +++ b/main.go @@ -73,10 +73,14 @@ func main() { }, ) if err := ctx.Run(); err != nil { - if errors.Is(err, exit.ErrAborted) || errors.Is(err, huh.ErrUserAborted) { + if errors.Is(err, huh.ErrTimeout) { + fmt.Fprintln(os.Stderr, err) + os.Exit(exit.StatusTimeout) + } + if errors.Is(err, huh.ErrUserAborted) { os.Exit(exit.StatusAborted) } - fmt.Println(err) + fmt.Fprintln(os.Stderr, err) os.Exit(1) } }