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