1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use ;
use OnceLock;
use crate;
// Global executable directory - computed once, cached forever
static EXE_DIR: = new;
/// Get the executable's directory (fallible).
///
/// **Use this only for libraries or specialized applications.** Most applications should
/// use [`crate::AppPath::try_new()`] for simpler, cleaner code.
///
/// # Examples
///
/// ```rust
/// use app_path::AppPath;
///
/// // Library with graceful error handling
/// match AppPath::try_new() {
/// Ok(app_base) => {
/// println!("Application base directory: {}", app_base.display());
/// let config = AppPath::with("config.toml");
/// }
/// Err(e) => {
/// eprintln!("Failed to get application base directory: {e}");
/// // Implement fallback strategy
/// }
/// }
///
/// // Use with ? operator for paths
/// fn get_config_dir() -> Result<std::path::PathBuf, Box<dyn std::error::Error>> {
/// let config = AppPath::try_with("config")?;
/// Ok(config.into())
/// }
/// ```
///
/// Once the executable directory is successfully determined by this function,
/// the result is cached globally and all subsequent calls will use the cached value.
/// This means that after the first successful call, `try_exe_dir()` will never return an error.
///
/// # Returns
///
/// * `Ok(&'static Path)` - The directory containing the current executable
/// * `Err(AppPathError)` - Failed to determine executable location
///
/// # Errors
///
/// Returns [`AppPathError`] if the executable location cannot be determined:
/// - [`AppPathError::ExecutableNotFound`] - `std::env::current_exe()` fails (extremely rare)
/// - [`AppPathError::InvalidExecutablePath`] - Executable path is empty (system corruption)
///
/// These errors represent unrecoverable system failures that occur at application startup.
/// After the first successful call, the executable directory is cached and this function
/// will never return an error.
///
/// # Performance
///
/// This function is highly optimized:
/// - **First call**: Determines and caches the executable directory
/// - **Subsequent calls**: Returns the cached result immediately (no system calls)
/// - **Thread-safe**: Safe to call from multiple threads concurrently
///
/// # Examples
///
/// ## Library Error Handling
///
/// ```rust
/// use app_path::AppPath;
///
/// // Handle the error explicitly
/// match AppPath::try_new() {
/// Ok(app_base) => {
/// println!("Application base directory: {}", app_base.display());
/// // Use app_base for further operations
/// }
/// Err(e) => {
/// eprintln!("Failed to get application base directory: {e}");
/// // Implement fallback strategy
/// }
/// }
/// ```
///
/// ## Use with ? Operator
///
/// ```rust
/// use app_path::AppPath;
///
/// // Use with the ? operator in functions that return Result
/// fn get_config_dir() -> Result<std::path::PathBuf, Box<dyn std::error::Error>> {
/// let config = AppPath::try_with("config")?;
/// Ok(config.into())
/// }
/// ```