app_path/app_path/
path_ops.rs

1use std::path::Path;
2
3use crate::AppPath;
4
5impl AppPath {
6    /// Joins additional path segments to create a new AppPath.
7    ///
8    /// This creates a new `AppPath` by joining the current path with additional segments.
9    /// The new path inherits the same resolution behavior as the original.
10    ///
11    /// # Examples
12    ///
13    /// ```rust
14    /// use app_path::AppPath;
15    ///
16    /// let data_dir = AppPath::with("data");
17    /// let users_db = data_dir.join("users.db");
18    /// let backups = data_dir.join("backups").join("daily");
19    ///
20    /// // Chain operations for complex paths
21    /// let log_file = AppPath::with("logs")
22    ///     .join("2024")
23    ///     .join("app.log");
24    /// ```
25    #[inline]
26    pub fn join(&self, path: impl AsRef<Path>) -> Self {
27        Self {
28            full_path: self.full_path.join(path),
29        }
30    }
31
32    /// Returns the parent directory as an AppPath, if it exists.
33    ///
34    /// Returns `None` if this path is a root directory or has no parent.
35    ///
36    /// # Examples
37    ///
38    /// ```rust
39    /// use app_path::AppPath;
40    ///
41    /// let config = AppPath::with("config/app.toml");
42    /// let config_dir = config.parent().unwrap();
43    ///
44    /// let logs_dir = AppPath::with("logs");
45    /// let _app_dir = logs_dir.parent(); // Points to exe directory
46    /// ```
47    #[inline]
48    pub fn parent(&self) -> Option<Self> {
49        self.full_path.parent().map(Self::with)
50    }
51
52    /// Creates a new AppPath with the specified file extension.
53    ///
54    /// If the path has an existing extension, it will be replaced.
55    /// If no extension exists, the new extension will be added.
56    ///
57    /// # Examples
58    ///
59    /// ```rust
60    /// use app_path::AppPath;
61    ///
62    /// let config = AppPath::with("config");
63    /// let config_toml = config.with_extension("toml");
64    /// let config_json = config.with_extension("json");
65    ///
66    /// let log_file = AppPath::with("app.log");
67    /// let backup_file = log_file.with_extension("bak");
68    /// ```
69    #[inline]
70    pub fn with_extension(&self, ext: &str) -> Self {
71        Self::with(self.full_path.with_extension(ext))
72    }
73
74    /// Consumes the `AppPath` and returns the internal `PathBuf`.
75    ///
76    /// This provides zero-cost extraction of the underlying `PathBuf` by moving
77    /// it out of the wrapper. This is useful when you need owned access to the
78    /// path for operations that consume `PathBuf`.
79    ///
80    /// # Examples
81    ///
82    /// ```rust
83    /// use app_path::AppPath;
84    /// use std::path::PathBuf;
85    ///
86    /// let app_path = AppPath::with("config.toml");
87    /// let path_buf: PathBuf = app_path.into_path_buf();
88    ///
89    /// // Now you have a regular PathBuf for operations that need ownership
90    /// assert!(path_buf.is_absolute());
91    /// ```
92    #[inline]
93    pub fn into_path_buf(self) -> std::path::PathBuf {
94        self.full_path
95    }
96
97    /// Consumes the `AppPath` and returns the internal `PathBuf`.
98    ///
99    /// This is an alias for [`into_path_buf()`](Self::into_path_buf) following
100    /// the standard Rust pattern for extracting wrapped values.
101    ///
102    /// # Examples
103    ///
104    /// ```rust
105    /// use app_path::AppPath;
106    /// use std::path::PathBuf;
107    ///
108    /// let app_path = AppPath::with("config.toml");
109    /// let path_buf: PathBuf = app_path.into_inner();
110    ///
111    /// // Now you have a regular PathBuf for operations that need ownership
112    /// assert!(path_buf.is_absolute());
113    /// ```
114    #[inline]
115    pub fn into_inner(self) -> std::path::PathBuf {
116        self.into_path_buf()
117    }
118
119    /// Returns the path as encoded bytes for low-level path operations.
120    ///
121    /// This provides access to the platform-specific byte representation of the path.
122    /// The returned bytes use platform-specific encoding and are only valid within
123    /// the same Rust version and target platform.
124    ///
125    /// **Safety Note**: These bytes should not be sent over networks, stored in files,
126    /// or used across different platforms, as the encoding is implementation-specific.
127    ///
128    /// Use cases include:
129    /// - **Platform-specific path parsing** with precise byte-level control
130    /// - **Custom path validation** that works with raw path bytes
131    /// - **Integration with platform APIs** that expect encoded path bytes
132    ///
133    /// # Examples
134    ///
135    /// ```rust
136    /// use app_path::AppPath;
137    ///
138    /// let config = AppPath::with("config.toml");
139    /// let bytes = config.to_bytes();
140    ///
141    /// // Platform-specific byte operations
142    /// assert!(!bytes.is_empty());
143    /// ```
144    #[inline]
145    pub fn to_bytes(&self) -> Vec<u8> {
146        // Use stable methods for getting bytes from OsStr
147        #[cfg(unix)]
148        {
149            use std::os::unix::ffi::OsStrExt;
150            self.as_os_str().as_bytes().to_vec()
151        }
152        #[cfg(windows)]
153        {
154            use std::os::windows::ffi::OsStrExt;
155            let wide: Vec<u16> = self.as_os_str().encode_wide().collect();
156            wide.iter().flat_map(|&w| w.to_le_bytes()).collect()
157        }
158        #[cfg(not(any(unix, windows)))]
159        {
160            // Fallback for other platforms - convert through string representation
161            self.to_string_lossy().as_bytes().to_vec()
162        }
163    }
164
165    /// Returns the path as owned encoded bytes.
166    ///
167    /// This consumes the AppPath and returns owned bytes using the same platform-specific
168    /// encoding as `to_bytes()`. The returned bytes are only valid within the same
169    /// Rust version and target platform.
170    ///
171    /// **Safety Note**: These bytes should not be sent over networks, stored in files,
172    /// or used across different platforms, as the encoding is implementation-specific.
173    ///
174    /// Use cases include:
175    /// - **Moving path data** across ownership boundaries
176    /// - **Temporary storage** of path bytes during processing
177    /// - **Platform-specific algorithms** requiring owned byte data
178    ///
179    /// # Examples
180    ///
181    /// ```rust
182    /// use app_path::AppPath;
183    ///
184    /// let config = AppPath::with("config.toml");
185    /// let owned_bytes = config.into_bytes();
186    ///
187    /// // Owned bytes can be moved and stored
188    /// assert!(!owned_bytes.is_empty());
189    /// ```
190    #[inline]
191    pub fn into_bytes(self) -> Vec<u8> {
192        // Use stable methods for getting bytes from OsString
193        #[cfg(unix)]
194        {
195            use std::os::unix::ffi::OsStringExt;
196            self.into_path_buf().into_os_string().into_vec()
197        }
198        #[cfg(windows)]
199        {
200            use std::os::windows::ffi::OsStrExt;
201            let wide: Vec<u16> = self.as_os_str().encode_wide().collect();
202            wide.iter().flat_map(|&w| w.to_le_bytes()).collect()
203        }
204        #[cfg(not(any(unix, windows)))]
205        {
206            // Fallback for other platforms - convert through string representation
207            self.to_string_lossy().into_owned().into_bytes()
208        }
209    }
210}