app_path/app_path/
traits.rs

1//! Trait implementations for `AppPath`.
2//!
3//! This module contains all the standard trait implementations that make `AppPath`
4//! work seamlessly with Rust's standard library and idiomatic code patterns.
5
6use crate::AppPath;
7use std::borrow::Borrow;
8use std::cmp::Ordering;
9use std::hash::{Hash, Hasher};
10use std::ops::Deref;
11use std::path::{Path, PathBuf};
12
13// === Core Display and Conversion Traits ===
14
15/// Default implementation returns the executable's directory.
16///
17/// This provides a natural default for AppPath - the directory where
18/// the executable is located.
19///
20/// # Examples
21///
22/// ```rust
23/// use app_path::AppPath;
24/// use std::path::Path;
25///
26/// let exe_dir = AppPath::default();
27/// let explicit = AppPath::new();
28/// assert_eq!(exe_dir.as_ref() as &Path, explicit.as_ref() as &Path);
29/// ```
30impl Default for AppPath {
31    #[inline]
32    fn default() -> Self {
33        Self::new()
34    }
35}
36
37// === Core Display and Conversion Traits ===
38
39impl std::fmt::Display for AppPath {
40    #[inline]
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        write!(f, "{}", self.full_path.display())
43    }
44}
45
46impl AsRef<Path> for AppPath {
47    #[inline]
48    fn as_ref(&self) -> &Path {
49        &self.full_path
50    }
51}
52
53// === Infallible From Implementations ===
54
55impl From<&str> for AppPath {
56    #[inline]
57    fn from(path: &str) -> Self {
58        Self::with(path)
59    }
60}
61
62impl From<String> for AppPath {
63    #[inline]
64    fn from(path: String) -> Self {
65        Self::with(path)
66    }
67}
68
69impl From<&String> for AppPath {
70    #[inline]
71    fn from(path: &String) -> Self {
72        Self::with(path)
73    }
74}
75
76impl From<&Path> for AppPath {
77    #[inline]
78    fn from(path: &Path) -> Self {
79        Self::with(path)
80    }
81}
82
83impl From<PathBuf> for AppPath {
84    #[inline]
85    fn from(path: PathBuf) -> Self {
86        Self::with(path)
87    }
88}
89
90impl From<&PathBuf> for AppPath {
91    #[inline]
92    fn from(path: &PathBuf) -> Self {
93        Self::with(path)
94    }
95}
96
97// === Additional Trait Implementations ===
98
99impl PartialEq for AppPath {
100    /// Compares two `AppPath` instances for equality based on their resolved paths.
101    ///
102    /// Two `AppPath` instances are considered equal if their full resolved paths
103    /// are identical, regardless of how they were constructed.
104    ///
105    /// # Examples
106    ///
107    /// ```rust
108    /// use app_path::AppPath;
109    ///
110    /// let path1 = AppPath::with("config.toml");
111    /// let path2 = AppPath::with("config.toml");
112    /// let path3 = AppPath::with("other.toml");
113    ///
114    /// assert_eq!(path1, path2);
115    /// assert_ne!(path1, path3);
116    /// ```
117    #[inline]
118    fn eq(&self, other: &Self) -> bool {
119        self.full_path == other.full_path
120    }
121}
122
123impl Eq for AppPath {}
124
125impl PartialOrd for AppPath {
126    /// Compares two `AppPath` instances lexicographically based on their resolved paths.
127    ///
128    /// The comparison is performed on the full resolved paths, providing consistent
129    /// ordering for sorting and collection operations.
130    ///
131    /// # Examples
132    ///
133    /// ```rust
134    /// use app_path::AppPath;
135    ///
136    /// let path1 = AppPath::with("a.txt");
137    /// let path2 = AppPath::with("b.txt");
138    ///
139    /// assert!(path1 < path2);
140    /// ```
141    #[inline]
142    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
143        Some(self.cmp(other))
144    }
145}
146
147impl Ord for AppPath {
148    /// Compares two `AppPath` instances lexicographically based on their resolved paths.
149    ///
150    /// This provides a total ordering that enables `AppPath` to be used in sorted
151    /// collections like `BTreeMap` and `BTreeSet`.
152    ///
153    /// # Examples
154    ///
155    /// ```rust
156    /// use app_path::AppPath;
157    /// use std::collections::BTreeSet;
158    ///
159    /// let mut paths = BTreeSet::new();
160    /// paths.insert(AppPath::with("config.toml"));
161    /// paths.insert(AppPath::with("data.db"));
162    /// paths.insert(AppPath::with("app.log"));
163    ///
164    /// // Paths are automatically sorted lexicographically
165    /// let sorted: Vec<_> = paths.into_iter().collect();
166    /// ```
167    #[inline]
168    fn cmp(&self, other: &Self) -> Ordering {
169        self.full_path.cmp(&other.full_path)
170    }
171}
172
173impl Hash for AppPath {
174    /// Computes a hash for the `AppPath` based on its resolved path.
175    ///
176    /// This enables `AppPath` to be used as keys in hash-based collections
177    /// like `HashMap` and `HashSet`. The hash is computed from the full
178    /// resolved path, ensuring consistent behavior.
179    ///
180    /// # Examples
181    ///
182    /// ```rust
183    /// use app_path::AppPath;
184    /// use std::collections::HashMap;
185    ///
186    /// let mut config_map = HashMap::new();
187    /// let config_path = AppPath::with("config.toml");
188    /// config_map.insert(config_path, "Configuration file");
189    /// ```
190    #[inline]
191    fn hash<H: Hasher>(&self, state: &mut H) {
192        self.full_path.hash(state);
193    }
194}
195
196impl Deref for AppPath {
197    type Target = Path;
198
199    /// Provides direct access to the underlying `Path` through deref coercion.
200    ///
201    /// This allows `AppPath` to be used directly with any API that expects a `&Path`,
202    /// making it a zero-cost abstraction in many contexts. All `Path` methods become
203    /// directly available on `AppPath` instances.
204    ///
205    /// # Examples
206    ///
207    /// ```rust
208    /// use app_path::AppPath;
209    ///
210    /// let app_path = AppPath::with("config.toml");
211    ///
212    /// // Direct access to Path methods through deref
213    /// assert_eq!(app_path.extension(), Some("toml".as_ref()));
214    /// assert_eq!(app_path.file_name(), Some("config.toml".as_ref()));
215    /// assert!(app_path.is_absolute());
216    ///
217    /// // Works with functions expecting &Path
218    /// fn process_path(path: &std::path::Path) {
219    ///     println!("Processing: {}", path.display());
220    /// }
221    /// process_path(&app_path); // Automatic deref coercion
222    ///
223    /// // For explicit &Path reference when needed
224    /// let path_ref: &std::path::Path = &app_path;        // Via deref
225    /// let path_ref2: &std::path::Path = app_path.as_ref(); // Via AsRef
226    /// ```
227    #[inline]
228    fn deref(&self) -> &Self::Target {
229        &self.full_path
230    }
231}
232
233impl Borrow<Path> for AppPath {
234    /// Allows `AppPath` to be borrowed as a `Path`.
235    ///
236    /// This enables `AppPath` to be used seamlessly in collections that are
237    /// keyed by `Path`, and allows for efficient lookups using `&Path` values.
238    ///
239    /// # Examples
240    ///
241    /// ```rust
242    /// use app_path::AppPath;
243    /// use std::collections::HashMap;
244    /// use std::path::Path;
245    ///
246    /// let mut path_map = HashMap::new();
247    /// let app_path = AppPath::with("config.toml");
248    /// path_map.insert(app_path, "config data");
249    ///
250    /// // Can look up using a &Path
251    /// let lookup_path = Path::new("relative/to/exe/config.toml");
252    /// // Note: This would only work if the paths actually match
253    /// ```
254    #[inline]
255    fn borrow(&self) -> &Path {
256        &self.full_path
257    }
258}
259
260// === Additional Conversion Traits ===
261
262impl AsRef<std::ffi::OsStr> for AppPath {
263    /// Converts `AppPath` to `&OsStr` for FFI operations.
264    ///
265    /// This is useful when interfacing with operating system APIs that require `OsStr`.
266    ///
267    /// # Examples
268    ///
269    /// ```rust
270    /// use app_path::AppPath;
271    /// use std::ffi::OsStr;
272    ///
273    /// let config = AppPath::with("config.toml");
274    /// let os_str: &OsStr = config.as_ref();
275    /// ```
276    #[inline]
277    fn as_ref(&self) -> &std::ffi::OsStr {
278        self.full_path.as_os_str()
279    }
280}
281
282impl From<AppPath> for PathBuf {
283    /// Converts `AppPath` to `PathBuf` for owned path operations.
284    ///
285    /// This moves the internal `PathBuf` out of the `AppPath`, providing
286    /// efficient conversion without cloning.
287    ///
288    /// # Examples
289    ///
290    /// ```rust
291    /// use app_path::AppPath;
292    /// use std::path::PathBuf;
293    ///
294    /// let config = AppPath::with("config.toml");
295    /// let path_buf: PathBuf = config.into();
296    /// ```
297    #[inline]
298    fn from(app_path: AppPath) -> Self {
299        app_path.full_path
300    }
301}
302
303impl From<AppPath> for std::ffi::OsString {
304    /// Converts `AppPath` to `OsString` for owned FFI operations.
305    ///
306    /// # Examples
307    ///
308    /// ```rust
309    /// use app_path::AppPath;
310    /// use std::ffi::OsString;
311    ///
312    /// let config = AppPath::with("config.toml");
313    /// let os_string: OsString = config.into();
314    /// ```
315    #[inline]
316    fn from(app_path: AppPath) -> Self {
317        app_path.full_path.into_os_string()
318    }
319}