1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use std::{ |
4 | ffi::{OsStr, OsString}, |
5 | mem, ptr, |
6 | }; |
7 | |
8 | use crate::{ffi, translate::*, GString}; |
9 | |
10 | // rustdoc-stripper-ignore-next |
11 | /// Same as [`get_prgname()`]. |
12 | /// |
13 | /// [`get_prgname()`]: fn.get_prgname.html |
14 | #[doc (alias = "get_program_name" )] |
15 | #[inline ] |
16 | pub fn program_name() -> Option<GString> { |
17 | prgname() |
18 | } |
19 | |
20 | #[doc (alias = "g_get_prgname" )] |
21 | #[doc (alias = "get_prgname" )] |
22 | #[inline ] |
23 | pub fn prgname() -> Option<GString> { |
24 | unsafe { from_glib_none(ptr:ffi::g_get_prgname()) } |
25 | } |
26 | |
27 | // rustdoc-stripper-ignore-next |
28 | /// Same as [`set_prgname()`]. |
29 | /// |
30 | /// [`set_prgname()`]: fn.set_prgname.html |
31 | #[inline ] |
32 | pub fn set_program_name(name: Option<impl IntoGStr>) { |
33 | set_prgname(name) |
34 | } |
35 | |
36 | #[doc (alias = "g_set_prgname" )] |
37 | #[inline ] |
38 | pub fn set_prgname(name: Option<impl IntoGStr>) { |
39 | name.run_with_gstr(|name: Option<&GStr>| unsafe { ffi::g_set_prgname(name.to_glib_none().0) }) |
40 | } |
41 | |
42 | #[doc (alias = "g_environ_getenv" )] |
43 | pub fn environ_getenv<K: AsRef<OsStr>>(envp: &[OsString], variable: K) -> Option<OsString> { |
44 | unsafe { |
45 | from_glib_none(ptr:ffi::g_environ_getenv( |
46 | envp.to_glib_none().0, |
47 | variable.as_ref().to_glib_none().0, |
48 | )) |
49 | } |
50 | } |
51 | |
52 | #[doc (alias = "g_mkstemp" )] |
53 | pub fn mkstemp<P: AsRef<std::path::Path>>(tmpl: P) -> i32 { |
54 | unsafe { |
55 | // NOTE: This modifies the string in place, which is fine here because |
56 | // to_glib_none() will create a temporary, NUL-terminated copy of the string. |
57 | ffi::g_mkstemp(tmpl.as_ref().to_glib_none().0) |
58 | } |
59 | } |
60 | |
61 | #[doc (alias = "g_mkstemp_full" )] |
62 | pub fn mkstemp_full(tmpl: impl AsRef<std::path::Path>, flags: i32, mode: i32) -> i32 { |
63 | unsafe { |
64 | // NOTE: This modifies the string in place, which is fine here because |
65 | // to_glib_none() will create a temporary, NUL-terminated copy of the string. |
66 | ffi::g_mkstemp_full(tmpl.as_ref().to_glib_none().0, flags, mode) |
67 | } |
68 | } |
69 | |
70 | #[doc (alias = "g_mkdtemp" )] |
71 | pub fn mkdtemp(tmpl: impl AsRef<std::path::Path>) -> Option<std::path::PathBuf> { |
72 | unsafe { |
73 | // NOTE: This modifies the string in place and returns it but does not free it |
74 | // if it returns NULL. |
75 | let tmpl: *mut i8 = tmpl.as_ref().to_glib_full(); |
76 | let res: *mut {unknown} = ffi::g_mkdtemp(tmpl); |
77 | if res.is_null() { |
78 | ffi::g_free(mem:tmpl as ffi::gpointer); |
79 | None |
80 | } else { |
81 | from_glib_full(ptr:res) |
82 | } |
83 | } |
84 | } |
85 | |
86 | #[doc (alias = "g_mkdtemp_full" )] |
87 | pub fn mkdtemp_full(tmpl: impl AsRef<std::path::Path>, mode: i32) -> Option<std::path::PathBuf> { |
88 | unsafe { |
89 | // NOTE: This modifies the string in place and returns it but does not free it |
90 | // if it returns NULL. |
91 | let tmpl: *mut i8 = tmpl.as_ref().to_glib_full(); |
92 | let res: *mut {unknown} = ffi::g_mkdtemp_full(tmpl, mode); |
93 | if res.is_null() { |
94 | ffi::g_free(mem:tmpl as ffi::gpointer); |
95 | None |
96 | } else { |
97 | from_glib_full(ptr:res) |
98 | } |
99 | } |
100 | } |
101 | |
102 | #[doc (alias = "g_file_get_contents" )] |
103 | pub fn file_get_contents( |
104 | filename: impl AsRef<std::path::Path>, |
105 | ) -> Result<crate::Slice<u8>, crate::Error> { |
106 | unsafe { |
107 | let mut contents: *mut u8 = ptr::null_mut(); |
108 | let mut length: MaybeUninit = mem::MaybeUninit::uninit(); |
109 | let mut error: *mut GError = ptr::null_mut(); |
110 | let _ = ffi::g_file_get_contents( |
111 | filename.as_ref().to_glib_none().0, |
112 | &mut contents, |
113 | length.as_mut_ptr(), |
114 | &mut error, |
115 | ); |
116 | if error.is_null() { |
117 | Ok(crate::Slice::from_glib_full_num( |
118 | ptr:contents, |
119 | len:length.assume_init() as _, |
120 | )) |
121 | } else { |
122 | Err(from_glib_full(ptr:error)) |
123 | } |
124 | } |
125 | } |
126 | |
127 | pub fn is_canonical_pspec_name(name: &str) -> bool { |
128 | name.as_bytes().iter().enumerate().all(|(i: usize, c: &u8)| { |
129 | i != 0 && (*c >= b'0' && *c <= b'9' || *c == b'-' ) |
130 | || (*c >= b'A' && *c <= b'Z' ) |
131 | || (*c >= b'a' && *c <= b'z' ) |
132 | }) |
133 | } |
134 | |
135 | #[doc (alias = "g_uri_escape_string" )] |
136 | pub fn uri_escape_string( |
137 | unescaped: impl IntoGStr, |
138 | reserved_chars_allowed: Option<impl IntoGStr>, |
139 | allow_utf8: bool, |
140 | ) -> crate::GString { |
141 | unescaped.run_with_gstr(|unescaped: &GStr| { |
142 | reserved_chars_allowed.run_with_gstr(|reserved_chars_allowed: Option<&GStr>| unsafe { |
143 | from_glib_full(ptr:ffi::g_uri_escape_string( |
144 | unescaped.to_glib_none().0, |
145 | reserved_chars_allowed.to_glib_none().0, |
146 | allow_utf8.into_glib(), |
147 | )) |
148 | }) |
149 | }) |
150 | } |
151 | |
152 | #[doc (alias = "g_uri_unescape_string" )] |
153 | pub fn uri_unescape_string( |
154 | escaped_string: impl IntoGStr, |
155 | illegal_characters: Option<impl IntoGStr>, |
156 | ) -> Option<crate::GString> { |
157 | escaped_string.run_with_gstr(|escaped_string: &GStr| { |
158 | illegal_characters.run_with_gstr(|illegal_characters: Option<&GStr>| unsafe { |
159 | from_glib_full(ptr:ffi::g_uri_unescape_string( |
160 | escaped_string.to_glib_none().0, |
161 | illegal_characters.to_glib_none().0, |
162 | )) |
163 | }) |
164 | }) |
165 | } |
166 | |
167 | #[doc (alias = "g_uri_parse_scheme" )] |
168 | pub fn uri_parse_scheme(uri: impl IntoGStr) -> Option<crate::GString> { |
169 | uri.run_with_gstr(|uri: &GStr| unsafe { |
170 | from_glib_full(ptr:ffi::g_uri_parse_scheme(uri.to_glib_none().0)) |
171 | }) |
172 | } |
173 | |
174 | #[doc (alias = "g_uri_unescape_segment" )] |
175 | pub fn uri_unescape_segment( |
176 | escaped_string: Option<impl IntoGStr>, |
177 | escaped_string_end: Option<impl IntoGStr>, |
178 | illegal_characters: Option<impl IntoGStr>, |
179 | ) -> Option<crate::GString> { |
180 | escaped_string.run_with_gstr(|escaped_string: Option<&GStr>| { |
181 | escaped_string_end.run_with_gstr(|escaped_string_end: Option<&GStr>| { |
182 | illegal_characters.run_with_gstr(|illegal_characters: Option<&GStr>| unsafe { |
183 | from_glib_full(ptr:ffi::g_uri_unescape_segment( |
184 | escaped_string.to_glib_none().0, |
185 | escaped_string_end.to_glib_none().0, |
186 | illegal_characters.to_glib_none().0, |
187 | )) |
188 | }) |
189 | }) |
190 | }) |
191 | } |
192 | |
193 | #[cfg (test)] |
194 | mod tests { |
195 | use std::{env, sync::Mutex, sync::OnceLock}; |
196 | |
197 | //Mutex to prevent run environment tests parallel |
198 | fn lock() -> &'static Mutex<()> { |
199 | static LOCK: OnceLock<Mutex<()>> = OnceLock::new(); |
200 | LOCK.get_or_init(|| Mutex::new(())) |
201 | } |
202 | |
203 | const VAR_NAME: &str = "function_environment_test" ; |
204 | |
205 | fn check_getenv(val: &str) { |
206 | let _data = lock().lock().unwrap(); |
207 | |
208 | env::set_var(VAR_NAME, val); |
209 | assert_eq!(env::var_os(VAR_NAME), Some(val.into())); |
210 | assert_eq!(crate::getenv(VAR_NAME), Some(val.into())); |
211 | |
212 | let environ = crate::environ(); |
213 | assert_eq!(crate::environ_getenv(&environ, VAR_NAME), Some(val.into())); |
214 | } |
215 | |
216 | fn check_setenv(val: &str) { |
217 | let _data = lock().lock().unwrap(); |
218 | |
219 | crate::setenv(VAR_NAME, val, true).unwrap(); |
220 | assert_eq!(env::var_os(VAR_NAME), Some(val.into())); |
221 | } |
222 | |
223 | #[test ] |
224 | fn getenv() { |
225 | check_getenv("Test" ); |
226 | check_getenv("Тест" ); // "Test" in Russian |
227 | } |
228 | |
229 | #[test ] |
230 | fn setenv() { |
231 | check_setenv("Test" ); |
232 | check_setenv("Тест" ); // "Test" in Russian |
233 | } |
234 | |
235 | #[test ] |
236 | fn test_filename_from_uri() { |
237 | use std::path::PathBuf; |
238 | |
239 | use crate::GString; |
240 | let uri: GString = "file:///foo/bar.txt" .into(); |
241 | if let Ok((filename, hostname)) = crate::filename_from_uri(&uri) { |
242 | assert_eq!(filename, PathBuf::from(r"/foo/bar.txt" )); |
243 | assert_eq!(hostname, None); |
244 | } else { |
245 | unreachable!(); |
246 | } |
247 | |
248 | let uri: GString = "file://host/foo/bar.txt" .into(); |
249 | if let Ok((filename, hostname)) = crate::filename_from_uri(&uri) { |
250 | assert_eq!(filename, PathBuf::from(r"/foo/bar.txt" )); |
251 | assert_eq!(hostname, Some(GString::from("host" ))); |
252 | } else { |
253 | unreachable!(); |
254 | } |
255 | } |
256 | |
257 | #[test ] |
258 | fn test_uri_parsing() { |
259 | use crate::GString; |
260 | assert_eq!( |
261 | crate::uri_parse_scheme("foo://bar" ), |
262 | Some(GString::from("foo" )) |
263 | ); |
264 | assert_eq!(crate::uri_parse_scheme("foo" ), None); |
265 | |
266 | let escaped = crate::uri_escape_string("&foo" , crate::NONE_STR, true); |
267 | assert_eq!(escaped, GString::from("%26foo" )); |
268 | |
269 | let unescaped = crate::uri_unescape_string(escaped.as_str(), crate::GStr::NONE); |
270 | assert_eq!(unescaped, Some(GString::from("&foo" ))); |
271 | |
272 | assert_eq!( |
273 | crate::uri_unescape_segment(Some("/foo" ), crate::NONE_STR, crate::NONE_STR), |
274 | Some(GString::from("/foo" )) |
275 | ); |
276 | assert_eq!( |
277 | crate::uri_unescape_segment(Some("/foo%" ), crate::NONE_STR, crate::NONE_STR), |
278 | None |
279 | ); |
280 | } |
281 | } |
282 | |