app_path/
error.rs

1use std::env::current_exe;
2use std::path::PathBuf;
3
4/// Error type for AppPath operations.
5///
6/// This enum represents the possible failures that can occur when determining
7/// the executable location. These errors are rare in practice and typically
8/// indicate fundamental system issues.
9///
10/// # When These Errors Occur
11///
12/// - **`ExecutableNotFound`**: When [`std::env::current_exe()`] fails
13///   - Very rare, but can happen in some embedded or heavily sandboxed environments
14///   - May occur if the executable has been deleted while running
15///   - Can happen in some containerized environments with unusual configurations
16///
17/// - **`InvalidExecutablePath`**: When the executable path is empty
18///   - Extremely rare, indicates a corrupted or broken system
19///   - May occur with custom or non-standard program loaders
20///
21/// These errors represent system-level failures that are typically unrecoverable
22/// for portable applications. Most applications should use the infallible API
23/// (`new()`, `exe_dir()`) and handle these rare cases through environment
24/// variables or fallback strategies.
25///
26/// # Examples
27///
28/// ```rust
29/// use app_path::{AppPath, AppPathError};
30///
31/// // Handle errors explicitly
32/// match AppPath::try_new("config.toml") {
33///     Ok(config) => {
34///         println!("Config path: {}", config.path().display());
35///     }
36///     Err(AppPathError::ExecutableNotFound(msg)) => {
37///         eprintln!("Cannot find executable: {}", msg);
38///         // Fallback to alternative configuration
39///     }
40///     Err(AppPathError::InvalidExecutablePath(msg)) => {
41///         eprintln!("Invalid executable path: {}", msg);
42///         // Fallback to alternative configuration
43///     }
44/// }
45/// ```
46#[derive(Debug, Clone, PartialEq, Eq)]
47pub enum AppPathError {
48    /// Failed to determine the current executable path.
49    ///
50    /// This error occurs when [`std::env::current_exe()`] fails, which is rare
51    /// but can happen in some embedded or heavily sandboxed environments.
52    ExecutableNotFound(String),
53
54    /// Executable path is empty or invalid.
55    ///
56    /// This error occurs when the system returns an empty executable path,
57    /// which is extremely rare and indicates a corrupted or broken system.
58    InvalidExecutablePath(String),
59}
60
61impl std::fmt::Display for AppPathError {
62    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63        match self {
64            AppPathError::ExecutableNotFound(msg) => {
65                write!(f, "Failed to determine executable location: {msg}")
66            }
67            AppPathError::InvalidExecutablePath(msg) => {
68                write!(f, "Invalid executable path: {msg}")
69            }
70        }
71    }
72}
73
74impl std::error::Error for AppPathError {}
75
76/// Try to determine the executable directory (fallible version).
77///
78/// This is the internal fallible initialization function that both the fallible
79/// and infallible APIs use. It handles all the edge cases properly without
80/// exposing them as errors to API users.
81pub(crate) fn try_exe_dir_init() -> Result<PathBuf, AppPathError> {
82    let exe = current_exe().map_err(|e| {
83        AppPathError::ExecutableNotFound(format!("std::env::current_exe() failed: {e}"))
84    })?;
85
86    if exe.as_os_str().is_empty() {
87        return Err(AppPathError::InvalidExecutablePath(
88            "Executable path is empty - unsupported environment".to_string(),
89        ));
90    }
91
92    // Handle edge case: executable at filesystem root (jailed environments, etc.)
93    // This is NOT an error - it's a valid case that should be handled internally
94    let dir = match exe.parent() {
95        Some(parent) => parent.to_path_buf(),
96        None => {
97            // If exe has no parent (e.g., running as "/init" or "C:\myapp.exe"),
98            // use the root directory itself
99            exe.ancestors().last().unwrap_or(&exe).to_path_buf()
100        }
101    };
102
103    Ok(dir)
104}