1#![allow(clippy::type_complexity)]
4
5mod local;
6
7use async_trait::async_trait;
8use futures::{future::LocalBoxFuture, ready};
9use pin_project::pin_project;
10use std::{
11 convert::TryFrom, error::Error, ffi, fmt, future::Future, io, pin::Pin, sync::Arc, task::{Context, Poll}
12};
13use widestring::U16String;
14
15use crate::pool::ProcessSend;
16
17pub use local::LocalFile;
18
19const PAGE_SIZE: usize = 10 * 1024 * 1024; #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
22pub struct OsString {
23 buf: U16String,
24}
25impl OsString {
26 pub fn new() -> Self {
27 Self {
28 buf: U16String::new(),
29 }
30 }
31 pub fn to_string_lossy(&self) -> String {
32 self.buf.to_string_lossy()
33 }
34 pub fn display(&self) -> impl fmt::Display + '_ {
35 struct Display<'a>(&'a OsString);
36 impl<'a> fmt::Display for Display<'a> {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 self.0.to_string_lossy().fmt(f)
39 }
40 }
41 Display(self)
42 }
43}
44impl From<Vec<u8>> for OsString {
45 fn from(from: Vec<u8>) -> Self {
46 Self {
47 buf: String::from_utf8(from)
48 .expect("Not yet imlemented: Handling non-UTF-8")
49 .into(),
50 } }
52}
53impl From<String> for OsString {
54 fn from(from: String) -> Self {
55 Self { buf: from.into() }
56 }
57}
58impl From<&str> for OsString {
59 fn from(from: &str) -> Self {
60 Self {
61 buf: U16String::from_str(from),
62 }
63 }
64}
65impl From<ffi::OsString> for OsString {
66 fn from(from: ffi::OsString) -> Self {
67 Self {
68 buf: U16String::from_os_str(&from),
69 }
70 }
71}
72impl From<&ffi::OsStr> for OsString {
73 fn from(from: &ffi::OsStr) -> Self {
74 Self {
75 buf: U16String::from_os_str(from),
76 }
77 }
78}
79pub struct InvalidOsString;
80impl TryFrom<OsString> for ffi::OsString {
81 type Error = InvalidOsString;
82
83 fn try_from(from: OsString) -> Result<Self, Self::Error> {
84 Ok(from.buf.to_os_string()) }
86}
87impl PartialEq<Vec<u8>> for OsString {
88 fn eq(&self, other: &Vec<u8>) -> bool {
89 self == &OsString::from(other.clone())
90 }
91}
92impl PartialEq<String> for OsString {
93 fn eq(&self, other: &String) -> bool {
94 self == &OsString::from(other.clone())
95 }
96}
97impl PartialEq<str> for OsString {
98 fn eq(&self, other: &str) -> bool {
99 self == &OsString::from(other)
100 }
101}
102impl PartialEq<ffi::OsString> for OsString {
103 fn eq(&self, other: &ffi::OsString) -> bool {
104 self == &OsString::from(other.clone())
105 }
106}
107impl PartialEq<ffi::OsStr> for OsString {
108 fn eq(&self, other: &ffi::OsStr) -> bool {
109 self == &OsString::from(other)
110 }
111}
112impl fmt::Debug for OsString {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 write!(f, "{}", self.display())
115 }
116}
117
118#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
119pub struct PathBuf {
120 components: Vec<OsString>,
121 file_name: Option<OsString>,
122}
123impl PathBuf {
124 pub fn new() -> Self {
125 Self {
126 components: Vec::new(),
127 file_name: None,
128 }
129 }
130 pub fn push<S>(&mut self, component: S)
131 where
132 S: Into<OsString>,
133 {
134 assert!(self.file_name.is_none());
135 self.components.push(component.into());
136 }
137 pub fn pop(&mut self) -> Option<OsString> {
138 assert!(self.file_name.is_none());
139 self.components.pop()
140 }
141 pub fn last(&self) -> Option<&OsString> {
142 assert!(self.file_name.is_none());
143 self.components.last()
144 }
145 pub fn set_file_name<S>(&mut self, file_name: Option<S>)
146 where
147 S: Into<OsString>,
148 {
149 self.file_name = file_name.map(Into::into);
150 }
151 pub fn is_file(&self) -> bool {
152 self.file_name.is_some()
153 }
154 pub fn file_name(&self) -> Option<&OsString> {
155 self.file_name.as_ref()
156 }
157 pub fn depth(&self) -> usize {
158 self.components.len()
159 }
160 pub fn iter<'a>(&'a self) -> impl Iterator<Item = &OsString> + 'a {
161 self.components.iter()
162 }
163 pub fn display(&self) -> impl fmt::Display + '_ {
164 struct Display<'a>(&'a PathBuf);
165 impl<'a> fmt::Display for Display<'a> {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 let mut res: fmt::Result = self
168 .0
169 .iter()
170 .try_for_each(|component| write!(f, "{}/", component.to_string_lossy()));
171 if let Some(file_name) = self.0.file_name() {
172 res = res.and_then(|()| write!(f, "{}", file_name.to_string_lossy()));
173 }
174 res
175 }
176 }
177 Display(self)
178 }
179}
180impl fmt::Debug for PathBuf {
181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182 write!(f, "{}", self.display())
183 }
184}
185
186#[async_trait(?Send)]
187pub trait Directory: File {
188 async fn partitions_filter<F>(
189 self, f: F,
190 ) -> Result<Vec<<Self as File>::Partition>, <Self as File>::Error>
191 where
192 F: FnMut(&PathBuf) -> bool;
193}
194
195#[async_trait(?Send)]
196pub trait File {
197 type Partition: Partition;
198 type Error: Error + Clone + PartialEq + 'static;
199
200 async fn partitions(self) -> Result<Vec<Self::Partition>, Self::Error>;
201}
202#[async_trait(?Send)]
203pub trait Partition: Clone + fmt::Debug + ProcessSend + 'static {
204 type Page: Page;
205 type Error: Error + Clone + PartialEq + ProcessSend + 'static;
206
207 async fn pages(self) -> Result<Vec<Self::Page>, Self::Error>;
208}
209#[allow(clippy::len_without_is_empty)]
210pub trait Page {
211 type Error: Error + Clone + PartialEq + Into<io::Error> + ProcessSend + 'static;
212
213 fn len(&self) -> LocalBoxFuture<'static, Result<u64, Self::Error>>;
214 fn read(
215 &self, offset: u64, len: usize,
216 ) -> LocalBoxFuture<'static, Result<Box<[u8]>, Self::Error>>;
217 fn write(
218 &self, offset: u64, buf: Box<[u8]>,
219 ) -> LocalBoxFuture<'static, Result<(), Self::Error>>;
220
221 fn reader(self) -> Reader<Self>
222 where
223 Self: Sized,
224 {
225 Reader::new(self)
226 }
227}
228
229impl<T: ?Sized> Page for &T
230where
231 T: Page,
232{
233 type Error = T::Error;
234
235 fn len(&self) -> LocalBoxFuture<'static, Result<u64, Self::Error>> {
236 (**self).len()
237 }
238 fn read(
239 &self, offset: u64, len: usize,
240 ) -> LocalBoxFuture<'static, Result<Box<[u8]>, Self::Error>> {
241 (**self).read(offset, len)
242 }
243 fn write(
244 &self, offset: u64, buf: Box<[u8]>,
245 ) -> LocalBoxFuture<'static, Result<(), Self::Error>> {
246 (**self).write(offset, buf)
247 }
248}
249impl<T: ?Sized> Page for Arc<T>
250where
251 T: Page,
252{
253 type Error = T::Error;
254
255 fn len(&self) -> LocalBoxFuture<'static, Result<u64, Self::Error>> {
256 (**self).len()
257 }
258 fn read(
259 &self, offset: u64, len: usize,
260 ) -> LocalBoxFuture<'static, Result<Box<[u8]>, Self::Error>> {
261 (**self).read(offset, len)
262 }
263 fn write(
264 &self, offset: u64, buf: Box<[u8]>,
265 ) -> LocalBoxFuture<'static, Result<(), Self::Error>> {
266 (**self).write(offset, buf)
267 }
268}
269
270#[pin_project]
271pub struct Reader<P>
272where
273 P: Page,
274{
275 #[pin]
276 page: P,
277 #[pin]
278 pending: Option<LocalBoxFuture<'static, Result<Box<[u8]>, P::Error>>>,
279 offset: u64,
280}
281#[allow(clippy::len_without_is_empty)]
282impl<P> Reader<P>
283where
284 P: Page,
285{
286 fn new(page: P) -> Self {
287 Self {
288 page,
289 pending: None,
290 offset: 0,
291 }
292 }
293}
294impl<P> futures::io::AsyncRead for Reader<P>
295where
296 P: Page,
297{
298 fn poll_read(
299 self: Pin<&mut Self>, cx: &mut Context, buf: &mut [u8],
300 ) -> Poll<io::Result<usize>> {
301 let mut self_ = self.project();
302 if self_.pending.is_none() {
303 let start = *self_.offset;
304 let len = buf.len();
305 let len = len.min(PAGE_SIZE);
306 let pending = self_.page.read(start, len);
307 *self_.pending = Some(pending);
308 }
309 let ret = ready!(self_.pending.as_mut().as_pin_mut().unwrap().poll(cx));
310 *self_.pending = None;
311 let ret = ret
312 .map(|buf_| {
313 buf[..buf_.len()].copy_from_slice(&buf_);
314 buf_.len()
315 })
316 .map_err(Into::into);
317 *self_.offset += u64::try_from(ret.as_ref().ok().copied().unwrap_or(0)).unwrap();
318 Poll::Ready(ret)
319 }
320}
321
322