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::{translate::*, GString, IntoGStr, IntoOptionalGStr}; |
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:envp.to_glib_none().0, |
47 | variable: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: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: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 i8 = 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 i8 = 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:filename.as_ref().to_glib_none().0, |
112 | &mut contents, |
113 | length: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:unescaped.to_glib_none().0, |
145 | reserved_chars_allowed:reserved_chars_allowed.to_glib_none().0, |
146 | allow_utf8: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:escaped_string.to_glib_none().0, |
161 | illegal_characters: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: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:escaped_string.to_glib_none().0, |
185 | escaped_string_end:escaped_string_end.to_glib_none().0, |
186 | illegal_characters:illegal_characters.to_glib_none().0, |
187 | )) |
188 | }) |
189 | }) |
190 | }) |
191 | } |
192 | |
193 | #[cfg (test)] |
194 | mod tests { |
195 | use std::{env, sync::Mutex}; |
196 | |
197 | //Mutex to prevent run environment tests parallel |
198 | static LOCK: once_cell::sync::Lazy<Mutex<()>> = once_cell::sync::Lazy::new(|| Mutex::new(())); |
199 | |
200 | const VAR_NAME: &str = "function_environment_test" ; |
201 | |
202 | fn check_getenv(val: &str) { |
203 | let _data = LOCK.lock().unwrap(); |
204 | |
205 | env::set_var(VAR_NAME, val); |
206 | assert_eq!(env::var_os(VAR_NAME), Some(val.into())); |
207 | assert_eq!(crate::getenv(VAR_NAME), Some(val.into())); |
208 | |
209 | let environ = crate::environ(); |
210 | assert_eq!(crate::environ_getenv(&environ, VAR_NAME), Some(val.into())); |
211 | } |
212 | |
213 | fn check_setenv(val: &str) { |
214 | let _data = LOCK.lock().unwrap(); |
215 | |
216 | crate::setenv(VAR_NAME, val, true).unwrap(); |
217 | assert_eq!(env::var_os(VAR_NAME), Some(val.into())); |
218 | } |
219 | |
220 | #[test ] |
221 | fn getenv() { |
222 | check_getenv("Test" ); |
223 | check_getenv("Тест" ); // "Test" in Russian |
224 | } |
225 | |
226 | #[test ] |
227 | fn setenv() { |
228 | check_setenv("Test" ); |
229 | check_setenv("Тест" ); // "Test" in Russian |
230 | } |
231 | |
232 | #[test ] |
233 | fn test_filename_from_uri() { |
234 | use std::path::PathBuf; |
235 | |
236 | use crate::GString; |
237 | let uri: GString = "file:///foo/bar.txt" .into(); |
238 | if let Ok((filename, hostname)) = crate::filename_from_uri(&uri) { |
239 | assert_eq!(filename, PathBuf::from(r"/foo/bar.txt" )); |
240 | assert_eq!(hostname, None); |
241 | } else { |
242 | unreachable!(); |
243 | } |
244 | |
245 | let uri: GString = "file://host/foo/bar.txt" .into(); |
246 | if let Ok((filename, hostname)) = crate::filename_from_uri(&uri) { |
247 | assert_eq!(filename, PathBuf::from(r"/foo/bar.txt" )); |
248 | assert_eq!(hostname, Some(GString::from("host" ))); |
249 | } else { |
250 | unreachable!(); |
251 | } |
252 | } |
253 | |
254 | #[test ] |
255 | fn test_uri_parsing() { |
256 | use crate::GString; |
257 | assert_eq!( |
258 | crate::uri_parse_scheme("foo://bar" ), |
259 | Some(GString::from("foo" )) |
260 | ); |
261 | assert_eq!(crate::uri_parse_scheme("foo" ), None); |
262 | |
263 | let escaped = crate::uri_escape_string("&foo" , crate::NONE_STR, true); |
264 | assert_eq!(escaped, GString::from("%26foo" )); |
265 | |
266 | let unescaped = crate::uri_unescape_string(escaped.as_str(), crate::GStr::NONE); |
267 | assert_eq!(unescaped, Some(GString::from("&foo" ))); |
268 | |
269 | assert_eq!( |
270 | crate::uri_unescape_segment(Some("/foo" ), crate::NONE_STR, crate::NONE_STR), |
271 | Some(GString::from("/foo" )) |
272 | ); |
273 | assert_eq!( |
274 | crate::uri_unescape_segment(Some("/foo%" ), crate::NONE_STR, crate::NONE_STR), |
275 | None |
276 | ); |
277 | } |
278 | } |
279 | |