| 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 | |