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}