Skip to main content

pistonite_cu/str/
osstring.rs

1use std::ffi::{OsStr, OsString};
2use std::path::{Path, PathBuf};
3
4use cu::Context as _;
5
6/// Convenience trait for working with [`OsStr`](std::ffi::OsStr)
7///
8/// See [File System Paths and Strings](trait@crate::str::PathExtension)
9pub trait OsStrExtension {
10    /// Get the path as UTF-8, error if it's not UTF-8
11    fn as_utf8(&self) -> cu::Result<&str>;
12}
13/// Convenience trait for working with [`OsString`](std::ffi::OsString)
14///
15/// See [File System Paths and Strings](trait@crate::str::PathExtension)
16pub trait OsStrExtensionOwned {
17    /// Get the path as UTF-8, error if it's not UTF-8
18    fn into_utf8(self) -> cu::Result<String>;
19}
20
21impl OsStrExtension for OsStr {
22    #[inline(always)]
23    fn as_utf8(&self) -> cu::Result<&str> {
24        // to_str is ok on all platforms, because Rust internally
25        // represent OsStrings on Windows as WTF-8
26        // see https://doc.rust-lang.org/src/std/sys_common/wtf8.rs.html
27        cu::check!(self.to_str(), "not utf-8: {self:?}")
28    }
29}
30
31impl OsStrExtension for Path {
32    #[inline(always)]
33    fn as_utf8(&self) -> cu::Result<&str> {
34        self.as_os_str().as_utf8()
35    }
36}
37
38impl OsStrExtensionOwned for OsString {
39    #[inline(always)]
40    fn into_utf8(self) -> cu::Result<String> {
41        match self.into_string() {
42            Ok(s) => Ok(s),
43            Err(e) => cu::bail!("not utf-8: {e:?}"),
44        }
45    }
46}
47impl OsStrExtensionOwned for PathBuf {
48    #[inline(always)]
49    fn into_utf8(self) -> cu::Result<String> {
50        self.into_os_string().into_utf8()
51    }
52}
53
54#[cfg(all(test, unix))]
55mod test {
56    use super::*;
57    use std::os::unix::ffi::OsStrExt as _;
58    #[test]
59    fn test_not_utf8() {
60        let s = OsStr::from_bytes(b"hello\xffworld");
61        let result = s.as_utf8().unwrap_err().to_string();
62        assert_eq!(result, r#"not utf-8: "hello\xFFworld""#);
63        let result = Path::new(s).as_utf8().unwrap_err().to_string();
64        assert_eq!(result, r#"not utf-8: "hello\xFFworld""#);
65
66        let s = s.to_owned();
67        let result = s.clone().into_utf8().unwrap_err().to_string();
68        assert_eq!(result, r#"not utf-8: "hello\xFFworld""#);
69        let result = PathBuf::from(s).into_utf8().unwrap_err().to_string();
70        assert_eq!(result, r#"not utf-8: "hello\xFFworld""#);
71    }
72    #[test]
73    fn test_utf8() {
74        let s = OsStr::from_bytes(b"hello world");
75        assert!(s.as_utf8().is_ok());
76        assert!(Path::new(s).as_utf8().is_ok());
77
78        let s = s.to_owned();
79        assert!(s.clone().into_utf8().is_ok());
80        assert!(PathBuf::from(s).into_utf8().is_ok());
81    }
82}
83
84#[cfg(all(test, windows))]
85mod test {
86    use super::*;
87    use std::os::windows::ffi::OsStringExt as _;
88    #[test]
89    fn test_not_utf8() {
90        let wide: &[u16] = &[
91            0x0068, 0x0065, 0x006C, 0x006C, 0x006F, 0xD800, 0x0077, 0x006F, 0x0072, 0x006C, 0x0064,
92        ];
93        let s = OsString::from_wide(wide);
94        let result = s.as_utf8().unwrap_err().to_string();
95        assert_eq!(result, r#"not utf-8: "hello\u{d800}world""#);
96        let result = Path::new(&s).as_utf8().unwrap_err().to_string();
97        assert_eq!(result, r#"not utf-8: "hello\u{d800}world""#);
98
99        let result = s.clone().into_utf8().unwrap_err().to_string();
100        assert_eq!(result, r#"not utf-8: "hello\u{d800}world""#);
101        let result = PathBuf::from(s).into_utf8().unwrap_err().to_string();
102        assert_eq!(result, r#"not utf-8: "hello\u{d800}world""#);
103    }
104    #[test]
105    fn test_utf8() {
106        let wide: &[u16] = &[
107            0x0068, 0x0065, 0x006C, 0x006C, 0x006F, 0x0020, 0x0077, 0x006F, 0x0072, 0x006C, 0x0064,
108        ];
109        let s = OsString::from_wide(wide);
110        assert!(s.as_utf8().is_ok());
111        assert!(Path::new(&s).as_utf8().is_ok());
112        assert!(s.clone().into_utf8().is_ok());
113        assert!(PathBuf::from(s).into_utf8().is_ok());
114    }
115}