base32_fs/
path_buf.rs

1use crate::Input;
2use crate::Output;
3
4use alloc::vec::Vec;
5use std::ffi::OsString;
6use std::path::Path;
7use std::path::PathBuf;
8
9/// An implementation of [`Output`](crate::Output) for file system paths.
10///
11/// Works on both Unix-like and Windows platforms.
12pub struct PathBufOutput {
13    #[cfg(windows)]
14    bytes: Vec<u16>,
15    #[cfg(unix)]
16    bytes: Vec<u8>,
17}
18
19impl PathBufOutput {
20    /// Transform into [`PathBuf`](std::path::PathBuf).
21    pub fn into_path_buf(self) -> PathBuf {
22        #[cfg(unix)]
23        let buf = OsString::from_vec(self.bytes).into();
24        #[cfg(windows)]
25        let buf = OsString::from_wide(&self.bytes[..]).into();
26        buf
27    }
28
29    /// Create path buffer from the supplied standard one.
30    ///
31    /// This method involves memory allocation only on Windows platforms.
32    pub fn from_path_buf(path: PathBuf) -> Self {
33        #[cfg(unix)]
34        let bytes = path.into_os_string().into_vec();
35        #[cfg(windows)]
36        let bytes = path.as_os_str().encode_wide().collect();
37        Self { bytes }
38    }
39
40    /// Create path buffer from the supplied path.
41    ///
42    /// This method involves memory allocation only on Unix platforms.
43    pub fn from_path(path: &Path) -> Self {
44        #[cfg(unix)]
45        let bytes = path.as_os_str().as_bytes().to_vec();
46        #[cfg(windows)]
47        let bytes = path.as_os_str().encode_wide().collect();
48        Self { bytes }
49    }
50
51    /// Create empty path buffer.
52    pub const fn new() -> Self {
53        Self { bytes: Vec::new() }
54    }
55
56    /// Create empty path buffer with desired capacity.
57    pub fn with_capacity(capacity: usize) -> Self {
58        Self {
59            bytes: Vec::with_capacity(capacity),
60        }
61    }
62}
63
64impl From<PathBufOutput> for PathBuf {
65    fn from(other: PathBufOutput) -> Self {
66        other.into_path_buf()
67    }
68}
69
70impl From<PathBuf> for PathBufOutput {
71    fn from(other: PathBuf) -> Self {
72        Self::from_path_buf(other)
73    }
74}
75
76impl Output for PathBufOutput {
77    fn push(&mut self, ch: u8) {
78        #[cfg(windows)]
79        let ch = ch.into();
80        self.bytes.push(ch)
81    }
82}
83
84/// An implementation of [`Input`](crate::Input) for file system paths.
85///
86/// Works on both Unix-like and Windows platforms.
87pub struct PathBufInput<'a> {
88    #[cfg(unix)]
89    inner: &'a [u8],
90    #[cfg(windows)]
91    inner: WideCharIter<'a>,
92}
93
94impl<'a> PathBufInput<'a> {
95    /// Create new input from the supplied path.
96    pub fn new(path: &'a Path) -> Self {
97        #[cfg(unix)]
98        let inner = path.as_os_str().as_bytes();
99        #[cfg(windows)]
100        let inner = WideCharIter::new(path.as_os_str().encode_wide());
101        Self { inner }
102    }
103}
104
105impl<'a> From<&'a Path> for PathBufInput<'a> {
106    fn from(other: &'a Path) -> Self {
107        Self::new(other)
108    }
109}
110
111impl Input<8> for PathBufInput<'_> {
112    fn next_chunk(&mut self) -> Option<&[u8]> {
113        Input::<8>::next_chunk(&mut self.inner)
114    }
115
116    fn remainder(&self) -> &[u8] {
117        Input::<8>::remainder(&self.inner)
118    }
119}
120
121#[cfg(unix)]
122mod unix {
123    pub use std::os::unix::ffi::OsStrExt;
124    pub use std::os::unix::ffi::OsStringExt;
125}
126
127#[cfg(unix)]
128use self::unix::*;
129
130#[cfg(windows)]
131mod windows {
132    use crate::Input;
133
134    pub use std::os::windows::ffi::EncodeWide;
135    pub use std::os::windows::ffi::OsStrExt;
136    pub use std::os::windows::ffi::OsStringExt;
137
138    pub struct WideCharIter<'a> {
139        iter: EncodeWide<'a>,
140        chunk: [u8; 8],
141        remainder_len: usize,
142    }
143
144    impl<'a> WideCharIter<'a> {
145        pub fn new(iter: EncodeWide<'a>) -> Self {
146            Self {
147                iter,
148                chunk: [0; 8],
149                remainder_len: 0,
150            }
151        }
152    }
153
154    impl<'a> Input<8> for WideCharIter<'a> {
155        fn next_chunk(&mut self) -> Option<&[u8]> {
156            for i in 0..8 {
157                match self.iter.next() {
158                    // Map to an invalid character to trigger an error.
159                    Some(x) => self.chunk[i] = x.try_into().unwrap_or(u8::MAX),
160                    None => {
161                        self.remainder_len = i;
162                        return None;
163                    }
164                }
165            }
166            Some(&self.chunk[..])
167        }
168
169        fn remainder(&self) -> &[u8] {
170            &self.chunk[..self.remainder_len]
171        }
172    }
173}
174
175#[cfg(windows)]
176use self::windows::*;