fuchsia_archive/
async_utf8_reader.rs1use crate::error::Error;
6use fuchsia_fs::file::{AsyncGetSize, AsyncReadAt};
7use futures::lock::Mutex;
8use std::sync::Arc;
9
10#[derive(Debug)]
13pub struct AsyncUtf8Reader<T>
14where
15 T: AsyncReadAt + AsyncGetSize + Unpin,
16{
17 reader: crate::async_read::AsyncReader<T>,
18}
19
20impl<T> AsyncUtf8Reader<T>
21where
22 T: AsyncReadAt + AsyncGetSize + Unpin,
23{
24 pub async fn new(source: T) -> Result<Self, Error> {
26 let ret = Self { reader: crate::async_read::AsyncReader::new(source).await? };
27 let () = ret.try_list().try_for_each(|r| r.map(|_| ()))?;
28 Ok(ret)
29 }
30
31 fn try_list(&self) -> impl ExactSizeIterator<Item = Result<crate::Utf8Entry<'_>, Error>> {
34 self.reader.list().map(|e| {
35 Ok(crate::Utf8Entry {
36 path: std::str::from_utf8(e.path).map_err(|err| Error::PathDataInvalidUtf8 {
37 source: err,
38 path: e.path.into(),
39 })?,
40 offset: e.offset,
41 length: e.length,
42 })
43 })
44 }
45
46 pub fn list(&self) -> impl ExactSizeIterator<Item = crate::Utf8Entry<'_>> {
48 self.try_list().map(|r| {
49 r.expect("AsyncUtf8Reader::new only succeeds if try_list succeeds for every element")
50 })
51 }
52
53 pub async fn read_file(&mut self, path: &str) -> Result<Vec<u8>, Error> {
56 self.reader.read_file(path.as_bytes()).await
57 }
58
59 pub fn into_source(self) -> T {
60 self.reader.into_source()
61 }
62}
63
64impl<T> AsyncUtf8Reader<Arc<Mutex<T>>>
65where
66 T: AsyncReadAt + AsyncGetSize + Unpin + Send,
67{
68 pub fn read_file_stream(
72 &self,
73 path: &str,
74 buffer_size: usize,
75 ) -> Result<
76 (u64, impl futures::stream::Stream<Item = Result<Vec<u8>, std::io::Error>> + Send),
77 Error,
78 > {
79 self.reader.read_file_stream(path.as_bytes(), buffer_size)
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use assert_matches::assert_matches;
87 use fuchsia_async as fasync;
88 use fuchsia_fs::file::Adapter;
89 use futures::io::Cursor;
90
91 #[fasync::run_singlethreaded(test)]
92 async fn new_rejects_non_utf8_path() {
93 let mut far_bytes = vec![];
94 let () = crate::write::write(
95 &mut far_bytes,
96 std::collections::BTreeMap::from_iter([(
97 b"\xff",
98 (0, Box::new("".as_bytes()) as Box<dyn std::io::Read>),
99 )]),
100 )
101 .unwrap();
102
103 assert_matches!(
104 AsyncUtf8Reader::new(Adapter::new(Cursor::new(far_bytes))).await,
105 Err(crate::Error::PathDataInvalidUtf8{source: _, path}) if path == b"\xff".to_vec()
106 );
107 }
108
109 #[fasync::run_singlethreaded(test)]
110 async fn list_does_not_panic() {
111 let mut far_bytes = vec![];
112 let () = crate::write::write(
113 &mut far_bytes,
114 std::collections::BTreeMap::from_iter([(
115 "valid-utf8",
116 (0, Box::new("".as_bytes()) as Box<dyn std::io::Read>),
117 )]),
118 )
119 .unwrap();
120
121 itertools::assert_equal(
122 AsyncUtf8Reader::new(Adapter::new(Cursor::new(far_bytes))).await.unwrap().list(),
123 [crate::Utf8Entry { path: "valid-utf8", offset: 4096, length: 0 }],
124 );
125 }
126
127 #[fasync::run_singlethreaded(test)]
128 async fn read_file() {
129 let mut far_bytes = vec![];
130 let () = crate::write::write(
131 &mut far_bytes,
132 std::collections::BTreeMap::from_iter([(
133 "valid-utf8",
134 (12, Box::new("test-content".as_bytes()) as Box<dyn std::io::Read>),
135 )]),
136 )
137 .unwrap();
138
139 assert_eq!(
140 AsyncUtf8Reader::new(Adapter::new(Cursor::new(far_bytes)))
141 .await
142 .unwrap()
143 .read_file("valid-utf8")
144 .await
145 .unwrap(),
146 b"test-content".to_vec()
147 );
148 }
149}