Skip to main content

sqry_cli/
error.rs

1//! CLI error types with custom exit codes
2//!
3//! This module defines error types that map to specific exit codes:
4//! - 0: Success
5//! - 1: Runtime error (default)
6//! - 2: Validation error (index corruption)
7//! - N: Pager exit code (when pager exits with non-zero)
8
9use std::fmt;
10
11/// CLI-specific error type with custom exit codes
12#[derive(Debug)]
13pub enum CliError {
14    /// Runtime error (exit code 1)
15    RuntimeError(anyhow::Error),
16
17    /// Pager exited with non-zero status (exit code from pager)
18    ///
19    /// This is used to propagate pager exit codes to the CLI exit code.
20    /// For example, if the user kills the pager with Ctrl-C (SIGINT),
21    /// this would propagate exit code 130 (128 + 2).
22    PagerExit(i32),
23}
24
25impl CliError {
26    /// Returns the appropriate exit code for this error
27    #[must_use]
28    pub fn exit_code(&self) -> i32 {
29        match self {
30            CliError::RuntimeError(_) => 1,
31            CliError::PagerExit(code) => *code,
32        }
33    }
34
35    /// Create a runtime error
36    #[allow(dead_code)]
37    #[must_use]
38    pub fn runtime(err: impl Into<anyhow::Error>) -> Self {
39        CliError::RuntimeError(err.into())
40    }
41
42    /// Create a pager exit error
43    ///
44    /// Use this when the pager exits with a non-zero status and you want
45    /// to propagate that exit code to the CLI.
46    #[must_use]
47    pub fn pager_exit(code: i32) -> Self {
48        CliError::PagerExit(code)
49    }
50}
51
52impl fmt::Display for CliError {
53    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54        match self {
55            CliError::RuntimeError(err) => write!(f, "{err}"),
56            CliError::PagerExit(code) => write!(f, "pager exited with code {code}"),
57        }
58    }
59}
60
61impl std::error::Error for CliError {
62    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
63        match self {
64            CliError::RuntimeError(err) => err.source(),
65            CliError::PagerExit(_) => None,
66        }
67    }
68}
69
70// Allow converting anyhow::Error to CliError (defaults to RuntimeError)
71impl From<anyhow::Error> for CliError {
72    fn from(err: anyhow::Error) -> Self {
73        CliError::RuntimeError(err)
74    }
75}