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
46impl From<CString> for FilePath<'_> {
47 fn from(value: CString) -> Self {
48 FilePath(Cow::Owned(value))
49 }
50}
51
52impl<'f> From<&'f CString> for FilePath<'f> {
53 fn from(value: &'f CString) -> Self {
54 FilePath(Cow::Borrowed(value.as_c_str()))
55 }
56}
57
58impl<'f> From<&'f OsStr> for FilePath<'f> {
59 fn from(value: &'f OsStr) -> FilePath<'f> {
60 FilePath(bytes_with_null(value.as_encoded_bytes()))
61 }
62}
63
64impl<'f> From<&'f OsString> for FilePath<'f> {
65 fn from(value: &'f OsString) -> FilePath<'f> {
66 FilePath(bytes_with_null(value.as_encoded_bytes()))
67 }
68}
69
70impl From<OsString> for FilePath<'_> {
71 fn from(value: OsString) -> Self {
72 FilePath(vec_to_cstr(value.as_encoded_bytes().to_vec()))
73 }
74}
75
76impl<'f> From<&'f PathBuf> for FilePath<'f> {
77 fn from(value: &'f PathBuf) -> FilePath<'f> {
78 FilePath::from(value.as_os_str())
79 }
80}
81
82impl From<PathBuf> for FilePath<'_> {
83 fn from(value: PathBuf) -> FilePath<'static> {
84 FilePath::from(OsString::from(value))
85 }
86}
87
88impl<'f> From<&'f Path> for FilePath<'f> {
89 fn from(value: &'f Path) -> Self {
90 Self::from(value.as_os_str())
91 }
92}
93
94impl<'f> From<&'f CStr> for FilePath<'f> {
95 fn from(value: &'f CStr) -> Self {
96 Self(Cow::Borrowed(value))
97 }
98}
99
100impl<'f> From<&'f str> for FilePath<'f> {
101 fn from(value: &'f str) -> Self {
102 Self::from(OsStr::new(value))
103 }
104}
105
106impl<'f> AsRef<FilePath<'f>> for FilePath<'f> {
107 fn as_ref(&self) -> &FilePath<'f> {
108 self
109 }
110}
111
112impl From<FilePath<'_>> for OsString {
113 fn from(value: FilePath<'_>) -> Self {
114 unsafe { OsString::from_encoded_bytes_unchecked(value.0.to_bytes().to_vec()) }
119 }
120}
121
122impl<'f> From<&'f FilePath<'f>> for &'f Path {
123 fn from(value: &'f FilePath<'f>) -> Self {
124 Path::new(value.0.as_ref().to_str().unwrap())
127 }
128}
129
130impl<'f> From<FilePath<'f>> for PathBuf {
131 fn from(value: FilePath<'f>) -> Self {
132 PathBuf::from(value.0.to_string_lossy().to_string())
133 }
134}
135
136pub fn vec_to_cstr(mut bytes: Vec<u8>) -> Cow<'static, CStr> {
145 if let Some(pos) = bytes.iter().position(|&b| b == 0) {
146 bytes.truncate(pos + 1);
147 } else {
148 bytes.push(0);
149 }
150 Cow::Owned(CString::from_vec_with_nul(bytes).unwrap())
152}
153
154fn bytes_with_null(bytes: &[u8]) -> Cow<'_, CStr> {
163 if let Ok(cstr) = CStr::from_bytes_until_nul(bytes) {
164 return Cow::Borrowed(cstr);
165 }
166 Cow::Owned(CString::new(bytes).unwrap())
168}
169
170#[cfg(test)]
171mod file_path_test {
172 use super::*;
173 use crate::zvariant::Signature;
174 use std::path::{Path, PathBuf};
175
176 #[test]
177 fn from_test() {
178 let path = Path::new("/hello/world");
179 let path_buf = PathBuf::from(path);
180 let osstr = OsStr::new("/hello/world");
181 let os_string = OsString::from("/hello/world");
182 let cstr = CStr::from_bytes_until_nul("/hello/world\0".as_bytes()).unwrap_or_default();
183 let cstring = CString::new("/hello/world").unwrap_or_default();
184
185 let p1 = FilePath::from(path);
186 let p2 = FilePath::from(path_buf);
187 let p3 = FilePath::from(osstr);
188 let p4 = FilePath::from(os_string);
189 let p5 = FilePath::from(cstr);
190 let p6 = FilePath::from(cstring);
191 let p7 = FilePath::from("/hello/world");
192
193 assert_eq!(p1, p2);
194 assert_eq!(p2, p3);
195 assert_eq!(p3, p4);
196 assert_eq!(p4, p5);
197 assert_eq!(p5, p6);
198 assert_eq!(p5, p7);
199 }
200
201 #[test]
202 fn filepath_signature() {
203 assert_eq!(
204 &Signature::static_array(&Signature::U8),
205 FilePath::SIGNATURE
206 );
207 }
208
209 #[test]
210 fn into_test() {
211 let first = PathBuf::from("/hello/world");
212 let third = OsString::from("/hello/world");
213 let fifth = Path::new("/hello/world");
214 let p = FilePath::from(first.clone());
215 let p2 = FilePath::from(third.clone());
216 let p3 = FilePath::from(fifth);
217 let second: PathBuf = p.into();
218 let forth: OsString = p2.into();
219 let sixth: &Path = (&p3).into();
220 assert_eq!(first, second);
221 assert_eq!(third, forth);
222 assert_eq!(fifth, sixth);
223 }
224
225 #[test]
226 fn vec_nul_termination() {
227 let v1 = vec![];
228 let v2 = vec![0x0];
229 let v3 = vec![0x1, 0x2, 0x0];
230 let v4 = vec![0x0, 0x0];
231 let v5 = vec![0x1, 0x0, 0x2, 0x0];
232
233 assert_eq!(
234 Cow::Borrowed(CStr::from_bytes_with_nul(&[0x0]).unwrap()),
235 vec_to_cstr(v1)
236 );
237 assert_eq!(
238 Cow::Borrowed(CStr::from_bytes_with_nul(&[0x0]).unwrap()),
239 vec_to_cstr(v2)
240 );
241 assert_eq!(
242 Cow::Borrowed(CStr::from_bytes_with_nul(&[0x1, 0x2, 0x0]).unwrap()),
243 vec_to_cstr(v3)
244 );
245 assert_eq!(
246 Cow::Borrowed(CStr::from_bytes_with_nul(&[0x0]).unwrap()),
247 vec_to_cstr(v4)
248 );
249 assert_eq!(
250 Cow::Borrowed(CStr::from_bytes_with_nul(&[0x1, 0x0]).unwrap()),
251 vec_to_cstr(v5)
252 );
253 }
254}