dope_core/io/fs/path/
mod.rs1mod error;
2mod ffi;
3
4use std::{os::unix, path::Path};
5
6pub use error::Error;
7use o3::{alloc, alloc::VecExt, mem::rc};
8
9use crate::io::fs;
10
11const MAX_COMPONENT_BYTES: usize = 255;
12const _: () = assert!(MAX_COMPONENT_BYTES == u8::MAX as usize);
13
14#[derive(Clone, Copy)]
15pub struct Component<'a> {
16 bytes: &'a [u8],
17}
18
19pub struct Owned {
20 bytes: Box<std::ffi::CStr>,
21}
22
23pub struct Confined {
24 pub(super) directory: fs::Dir,
25 pub(super) path: Owned,
26}
27
28#[derive(Clone, Copy, Debug, Eq, PartialEq)]
29pub enum ConfineError {
30 Allocation(alloc::Error),
31 Retain(rc::Error),
32}
33
34#[derive(Clone, Copy, Debug, Eq, PartialEq)]
35pub enum CopyError {
36 Nul { position: usize },
37 Allocation(alloc::Error),
38}
39
40pub trait CStrExt: Sized {
42 fn try_copy(path: &Path) -> Result<Self, CopyError>;
43}
44
45impl CStrExt for Box<std::ffi::CStr> {
46 #[inline]
47 fn try_copy(path: &Path) -> Result<Self, CopyError> {
48 let bytes = unix::ffi::OsStrExt::as_bytes(path.as_os_str());
49 if let Ok(prefix) = std::ffi::CStr::from_bytes_until_nul(bytes) {
50 return Err(CopyError::Nul {
51 position: prefix.to_bytes().len(),
52 });
53 }
54 try_copy(bytes).map_err(CopyError::Allocation)
55 }
56}
57
58#[derive(Clone, Copy)]
59pub struct Entry<'a> {
60 directory: &'a fs::Dir,
61 name: &'a Owned,
62}
63
64#[derive(Clone, Copy)]
65pub struct Rename<'a> {
66 directory: &'a fs::Dir,
67 old: &'a Owned,
68 new: &'a Owned,
69}
70
71impl<'a> Component<'a> {
72 pub fn parse_os(value: &'a std::ffi::OsStr) -> Result<Self, Error> {
73 Self::parse_bytes(unix::ffi::OsStrExt::as_bytes(value))
74 }
75
76 fn parse_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
77 if bytes.is_empty() {
78 return Err(Error::Empty);
79 }
80 if bytes == b".." {
81 return Err(Error::Parent { offset: 0 });
82 }
83 if bytes == b"." {
84 return Err(Error::NoComponent { length: 1 });
85 }
86 if let Some(offset) = bytes.iter().position(|byte| *byte == b'/') {
87 return Err(Error::Nested { offset });
88 }
89 if let Some(pos) = bytes.iter().position(|byte| *byte == 0) {
90 return Err(Error::Nul { position: pos });
91 }
92 if bytes.len() > MAX_COMPONENT_BYTES {
93 return Err(Error::Component {
94 offset: 0,
95 length: bytes.len(),
96 limit: MAX_COMPONENT_BYTES,
97 });
98 }
99 Ok(Self { bytes })
100 }
101
102 pub fn try_to_owned(self) -> Result<Owned, alloc::Error> {
103 let bytes = try_copy(self.bytes)?;
104 Ok(Owned { bytes })
105 }
106}
107
108#[inline]
110fn try_copy(base: &[u8]) -> Result<Box<std::ffi::CStr>, alloc::Error> {
111 let len = base.len().checked_add(1).ok_or(alloc::Error::Overflow)?;
112 let mut bytes: Vec<u8> = VecExt::try_new(len)?;
113 bytes.extend_from_slice(base);
114 bytes.push(0);
115 Ok(ffi::finish(bytes))
116}
117
118impl Owned {
119 pub(crate) fn as_c_str(&self) -> &std::ffi::CStr {
120 &self.bytes
121 }
122}
123
124impl Confined {
125 pub(in crate::io::fs) fn new(dir: fs::Dir, path: Owned) -> Self {
126 Self {
127 directory: dir,
128 path,
129 }
130 }
131
132 pub(crate) fn view(&self) -> Entry<'_> {
133 Entry::new(&self.directory, &self.path)
134 }
135}
136
137impl Confined {
138 pub fn into_owned(self) -> Owned {
139 self.path
140 }
141}
142
143impl<'a> Entry<'a> {
144 pub(in crate::io::fs) fn new(dir: &'a fs::Dir, name: &'a Owned) -> Self {
145 Self {
146 directory: dir,
147 name,
148 }
149 }
150
151 pub(crate) fn directory(self) -> &'a fs::Dir {
152 self.directory
153 }
154
155 pub(crate) fn name(self) -> &'a std::ffi::CStr {
156 self.name.as_c_str()
157 }
158}
159
160impl<'a> Rename<'a> {
161 pub(in crate::io::fs) fn new(dir: &'a fs::Dir, old: &'a Owned, new: &'a Owned) -> Self {
162 Self {
163 directory: dir,
164 old,
165 new,
166 }
167 }
168
169 pub(crate) fn directory(self) -> &'a fs::Dir {
170 self.directory
171 }
172
173 pub(crate) fn old(self) -> &'a std::ffi::CStr {
174 self.old.as_c_str()
175 }
176
177 pub(crate) fn new_name(self) -> &'a std::ffi::CStr {
178 self.new.as_c_str()
179 }
180}
181
182const _: () = {
183 assert!(std::mem::size_of::<Owned>() == 2 * std::mem::size_of::<usize>());
184 assert!(std::mem::size_of::<Confined>() == 3 * std::mem::size_of::<usize>());
185 assert!(std::mem::size_of::<Entry<'_>>() == 2 * std::mem::size_of::<usize>());
186 assert!(std::mem::size_of::<Rename<'_>>() == 3 * std::mem::size_of::<usize>());
187};