1use serde::{Deserialize, Serialize};
2use std::{
3 borrow::Cow,
4 ffi::{CStr, CString, OsStr, OsString},
5 path::{Path, PathBuf},
6};
7
8use crate::Type;
9
10#[derive(Type, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Clone, Ord, PartialOrd)]
37#[zvariant(signature = "ay")]
38pub struct FilePath<'f>(Cow<'f, CStr>);
39
40impl<'f> FilePath<'f> {
41 pub fn new(cow: Cow<'f, CStr>) -> Self {
42 Self(cow)
43 }
44
45 pub fn to_string_lossy(&self) -> Cow<'_, str> {
49 self.0.to_string_lossy()
50 }
51}
52
53impl From<CString> for FilePath<'_> {
54 fn from(value: CString) -> Self {
55 FilePath(Cow::Owned(value))
56 }
57}
58
59impl<'f> From<&'f CString> for FilePath<'f> {
60 fn from(value: &'f CString) -> Self {
61 FilePath(Cow::Borrowed(value.as_c_str()))
62 }
63}
64
65impl<'f> From<&'f OsStr> for FilePath<'f> {
66 fn from(value: &'f OsStr) -> FilePath<'f> {
67 FilePath(bytes_with_null(value.as_encoded_bytes()))
68 }
69}
70
71impl<'f> From<&'f OsString> for FilePath<'f> {
72 fn from(value: &'f OsString) -> FilePath<'f> {
73 FilePath(bytes_with_null(value.as_encoded_bytes()))
74 }
75}
76
77impl From<OsString> for FilePath<'_> {
78 fn from(value: OsString) -> Self {
79 #[allow(deprecated)]
80 FilePath(vec_to_cstr(value.as_encoded_bytes().to_vec()))
81 }
82}
83
84impl<'f> From<&'f PathBuf> for FilePath<'f> {
85 fn from(value: &'f PathBuf) -> FilePath<'f> {
86 FilePath::from(value.as_os_str())
87 }
88}
89
90impl From<PathBuf> for FilePath<'_> {
91 fn from(value: PathBuf) -> FilePath<'static> {
92 FilePath::from(OsString::from(value))
93 }
94}
95
96impl<'f> From<&'f Path> for FilePath<'f> {
97 fn from(value: &'f Path) -> Self {
98 Self::from(value.as_os_str())
99 }
100}
101
102impl<'f> From<&'f CStr> for FilePath<'f> {
103 fn from(value: &'f CStr) -> Self {
104 Self(Cow::Borrowed(value))
105 }
106}
107
108impl<'f> From<&'f str> for FilePath<'f> {
109 fn from(value: &'f str) -> Self {
110 Self::from(OsStr::new(value))
111 }
112}
113
114impl<'f> AsRef<FilePath<'f>> for FilePath<'f> {
115 fn as_ref(&self) -> &FilePath<'f> {
116 self
117 }
118}
119
120impl From<FilePath<'_>> for OsString {
121 fn from(value: FilePath<'_>) -> Self {
122 unsafe { OsString::from_encoded_bytes_unchecked(value.0.to_bytes().to_vec()) }
127 }
128}
129
130impl<'f> From<&'f FilePath<'f>> for &'f Path {
131 fn from(value: &'f FilePath<'f>) -> Self {
132 Path::new(value.0.as_ref().to_str().unwrap())
135 }
136}
137
138impl<'f> From<FilePath<'f>> for PathBuf {
139 fn from(value: FilePath<'f>) -> Self {
140 PathBuf::from(value.0.to_string_lossy().to_string())
141 }
142}
143
144#[doc(hidden)]
153#[deprecated(
154 since = "5.9.0",
155 note = "This function was never meant to be public and will be removed in a future release."
156)]
157pub fn vec_to_cstr(mut bytes: Vec<u8>) -> Cow<'static, CStr> {
158 if let Some(pos) = bytes.iter().position(|&b| b == 0) {
159 bytes.truncate(pos + 1);
160 } else {
161 bytes.push(0);
162 }
163 Cow::Owned(CString::from_vec_with_nul(bytes).unwrap())
165}
166
167fn bytes_with_null(bytes: &[u8]) -> Cow<'_, CStr> {
176 if let Ok(cstr) = CStr::from_bytes_until_nul(bytes) {
177 return Cow::Borrowed(cstr);
178 }
179 Cow::Owned(CString::new(bytes).unwrap())
181}
182
183#[cfg(test)]
184mod file_path_test {
185 use super::*;
186 use crate::zvariant::Signature;
187 use std::path::{Path, PathBuf};
188
189 #[test]
190 fn from_test() {
191 let path = Path::new("/hello/world");
192 let path_buf = PathBuf::from(path);
193 let osstr = OsStr::new("/hello/world");
194 let os_string = OsString::from("/hello/world");
195 let cstr = CStr::from_bytes_until_nul("/hello/world\0".as_bytes()).unwrap_or_default();
196 let cstring = CString::new("/hello/world").unwrap_or_default();
197
198 let p1 = FilePath::from(path);
199 let p2 = FilePath::from(path_buf);
200 let p3 = FilePath::from(osstr);
201 let p4 = FilePath::from(os_string);
202 let p5 = FilePath::from(cstr);
203 let p6 = FilePath::from(cstring);
204 let p7 = FilePath::from("/hello/world");
205
206 assert_eq!(p1, p2);
207 assert_eq!(p2, p3);
208 assert_eq!(p3, p4);
209 assert_eq!(p4, p5);
210 assert_eq!(p5, p6);
211 assert_eq!(p5, p7);
212 }
213
214 #[test]
215 fn filepath_signature() {
216 assert_eq!(
217 &Signature::static_array(&Signature::U8),
218 FilePath::SIGNATURE
219 );
220 }
221
222 #[test]
223 fn into_test() {
224 let first = PathBuf::from("/hello/world");
225 let third = OsString::from("/hello/world");
226 let fifth = Path::new("/hello/world");
227 let p = FilePath::from(first.clone());
228 let p2 = FilePath::from(third.clone());
229 let p3 = FilePath::from(fifth);
230 let second: PathBuf = p.into();
231 let forth: OsString = p2.into();
232 let sixth: &Path = (&p3).into();
233 assert_eq!(first, second);
234 assert_eq!(third, forth);
235 assert_eq!(fifth, sixth);
236 }
237
238 #[test]
239 fn vec_nul_termination() {
240 #[allow(deprecated)]
241 fn call_vec_to_cstr(v: Vec<u8>) -> Cow<'static, CStr> {
242 vec_to_cstr(v)
243 }
244 let v1 = vec![];
245 let v2 = vec![0x0];
246 let v3 = vec![0x1, 0x2, 0x0];
247 let v4 = vec![0x0, 0x0];
248 let v5 = vec![0x1, 0x0, 0x2, 0x0];
249
250 assert_eq!(
251 Cow::Borrowed(CStr::from_bytes_with_nul(&[0x0]).unwrap()),
252 call_vec_to_cstr(v1)
253 );
254 assert_eq!(
255 Cow::Borrowed(CStr::from_bytes_with_nul(&[0x0]).unwrap()),
256 call_vec_to_cstr(v2)
257 );
258 assert_eq!(
259 Cow::Borrowed(CStr::from_bytes_with_nul(&[0x1, 0x2, 0x0]).unwrap()),
260 call_vec_to_cstr(v3)
261 );
262 assert_eq!(
263 Cow::Borrowed(CStr::from_bytes_with_nul(&[0x0]).unwrap()),
264 call_vec_to_cstr(v4)
265 );
266 assert_eq!(
267 Cow::Borrowed(CStr::from_bytes_with_nul(&[0x1, 0x0]).unwrap()),
268 call_vec_to_cstr(v5)
269 );
270 }
271}