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}