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}