Skip to main content

proc_cli/
error.rs

1//! Error types for proc CLI
2//!
3//! Provides structured error handling with helpful suggestions for users.
4
5use thiserror::Error;
6
7/// Main error type for proc operations
8#[derive(Error, Debug)]
9pub enum ProcError {
10    /// No process found matching the given target
11    #[error("No process found matching '{0}'\n  Try: proc list to list all processes")]
12    ProcessNotFound(String),
13
14    /// No process is listening on the specified port
15    #[error("No process listening on port {0}\n  Try: proc ports")]
16    PortNotFound(u16),
17
18    /// Insufficient permissions to operate on the process
19    #[error("Permission denied for PID {0}\n  Try: sudo proc <command>")]
20    PermissionDenied(u32),
21
22    /// User provided invalid input or arguments
23    #[error("Invalid input: {0}")]
24    InvalidInput(String),
25
26    /// An underlying system call failed
27    #[error("System error: {0}")]
28    SystemError(String),
29
30    /// The operation exceeded the allowed time limit
31    #[error("Operation timed out: {0}")]
32    Timeout(String),
33
34    /// Failed to parse input or system output
35    #[error("Failed to parse: {0}")]
36    ParseError(String),
37
38    /// Feature is not available on the current platform
39    #[error("Not supported on this platform: {0}")]
40    NotSupported(String),
41
42    /// The target process terminated during the operation
43    #[error("Process {0} is no longer running")]
44    ProcessGone(u32),
45
46    /// Failed to send a signal to the process
47    #[error("Signal failed: {0}")]
48    SignalError(String),
49
50    /// The command already printed its result, but the operation did not fully
51    /// succeed (some processes failed, a port stayed busy, a wait timed out).
52    /// `main` exits with `exit_code` without printing anything further.
53    #[error("{message}")]
54    Reported {
55        /// Human-readable summary of what did not succeed
56        message: String,
57        /// Exit code to return
58        exit_code: ExitCode,
59    },
60}
61
62impl ProcError {
63    /// Creates a [`ProcError::Reported`] for a result that has already been printed.
64    pub fn reported(exit_code: ExitCode, message: impl Into<String>) -> Self {
65        ProcError::Reported {
66            message: message.into(),
67            exit_code,
68        }
69    }
70}
71
72impl From<std::io::Error> for ProcError {
73    fn from(err: std::io::Error) -> Self {
74        match err.kind() {
75            std::io::ErrorKind::PermissionDenied => ProcError::PermissionDenied(0),
76            std::io::ErrorKind::NotFound => ProcError::ProcessNotFound("unknown".to_string()),
77            _ => ProcError::SystemError(err.to_string()),
78        }
79    }
80}
81
82impl From<serde_json::Error> for ProcError {
83    fn from(err: serde_json::Error) -> Self {
84        ProcError::ParseError(err.to_string())
85    }
86}
87
88impl From<regex::Error> for ProcError {
89    fn from(err: regex::Error) -> Self {
90        ProcError::InvalidInput(format!("Invalid pattern: {}", err))
91    }
92}
93
94impl From<dialoguer::Error> for ProcError {
95    fn from(err: dialoguer::Error) -> Self {
96        ProcError::SystemError(format!("Dialog error: {}", err))
97    }
98}
99
100/// Result type alias for proc operations
101pub type Result<T> = std::result::Result<T, ProcError>;
102
103/// Exit codes for CLI
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105pub enum ExitCode {
106    /// Operation completed successfully
107    Success = 0,
108    /// A general error occurred
109    GeneralError = 1,
110    /// The requested process or port was not found
111    NotFound = 2,
112    /// Operation requires elevated privileges
113    PermissionDenied = 3,
114    /// Invalid arguments or input provided
115    InvalidInput = 4,
116}
117
118impl From<&ProcError> for ExitCode {
119    fn from(err: &ProcError) -> Self {
120        match err {
121            ProcError::ProcessNotFound(_) | ProcError::PortNotFound(_) => ExitCode::NotFound,
122            ProcError::PermissionDenied(_) => ExitCode::PermissionDenied,
123            ProcError::InvalidInput(_) => ExitCode::InvalidInput,
124            ProcError::Reported { exit_code, .. } => *exit_code,
125            _ => ExitCode::GeneralError,
126        }
127    }
128}