Skip to main content

cli_ui/prompt/
error.rs

1//! Error type for prompt operations.
2
3use std::fmt;
4
5/// Error returned by prompt `.run()` methods.
6#[derive(Debug)]
7pub enum PromptError {
8    /// User pressed Ctrl+C or Ctrl+D.
9    Interrupted,
10    /// I/O error reading from stdin or writing to stderr.
11    Io(std::io::Error),
12}
13
14impl fmt::Display for PromptError {
15    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16        match self {
17            Self::Interrupted => write!(f, "prompt interrupted by user"),
18            Self::Io(e) => write!(f, "prompt I/O error: {e}"),
19        }
20    }
21}
22
23impl std::error::Error for PromptError {
24    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
25        match self {
26            Self::Io(e) => Some(e),
27            _ => None,
28        }
29    }
30}
31
32impl From<std::io::Error> for PromptError {
33    fn from(e: std::io::Error) -> Self {
34        Self::Io(e)
35    }
36}
37
38/// Result alias for prompt operations.
39pub type Result<T> = std::result::Result<T, PromptError>;
40
41/// `true` when the error came from Ctrl+C / Ctrl+D / Escape.
42pub fn is_cancel(err: &PromptError) -> bool {
43    matches!(err, PromptError::Interrupted)
44}
45
46/// Extension trait: gracefully end the process when the user cancels a prompt.
47///
48/// `or_cancel(msg)` prints `msg` (or the global [`settings::cancel`](super::settings::Settings::cancel)
49/// when omitted) using the framed `■  …` style, then `exit(0)`. Any other
50/// error is unwrapped — matching how clack's `isCancel` flow is usually used.
51///
52/// # Example
53/// ```no_run
54/// use cli_ui::prompt::{text, OnCancel};
55///
56/// let name = text("Your name").run().or_cancel("See you next time.");
57/// println!("hello {name}");
58/// ```
59pub trait OnCancel<T> {
60    /// Exit cleanly on cancellation with the given message.
61    fn or_cancel(self, message: &str) -> T;
62
63    /// Exit cleanly on cancellation using the global default message
64    /// from [`super::settings`].
65    fn or_cancel_default(self) -> T;
66}
67
68impl<T> OnCancel<T> for Result<T> {
69    fn or_cancel(self, message: &str) -> T {
70        match self {
71            Ok(v) => v,
72            Err(PromptError::Interrupted) => {
73                super::cancel(message);
74                std::process::exit(0);
75            }
76            Err(e) => panic!("{e}"),
77        }
78    }
79    fn or_cancel_default(self) -> T {
80        let msg = super::settings::get().cancel;
81        self.or_cancel(&msg)
82    }
83}