fuchsia_pkg/
package_directory.rs1#![cfg(target_os = "fuchsia")]
6
7use crate::{
10 MetaContents, MetaContentsError, MetaPackage, MetaPackageError, MetaSubpackages,
11 MetaSubpackagesError,
12};
13use fidl::endpoints::ServerEnd;
14use fidl_fuchsia_io as fio;
15use fuchsia_hash::{Hash, ParseHashError};
16use thiserror::Error;
17use version_history::AbiRevision;
18use zx_status;
19
20pub use fuchsia_fs::file::ReadError;
22pub use fuchsia_fs::node::{CloneError, CloseError, OpenError};
23
24#[derive(Debug, Error)]
26#[allow(missing_docs)]
27pub enum ReadHashError {
28 #[error("while reading 'meta/'")]
29 Read(#[source] ReadError),
30
31 #[error("while parsing 'meta/'")]
32 Parse(#[source] ParseHashError),
33}
34
35#[derive(Debug, Error)]
37#[allow(missing_docs)]
38pub enum LoadMetaPackageError {
39 #[error("while reading 'meta/package'")]
40 Read(#[source] ReadError),
41
42 #[error("while parsing 'meta/package'")]
43 Parse(#[source] MetaPackageError),
44}
45
46#[derive(Debug, Error)]
49#[allow(missing_docs)]
50pub enum LoadMetaSubpackagesError {
51 #[error("while reading '{}'", MetaSubpackages::PATH)]
52 Read(#[source] ReadError),
53
54 #[error("while parsing '{}'", MetaSubpackages::PATH)]
55 Parse(#[source] MetaSubpackagesError),
56}
57
58#[derive(Debug, Error)]
60#[allow(missing_docs)]
61pub enum LoadMetaContentsError {
62 #[error("while reading 'meta/contents'")]
63 Read(#[source] ReadError),
64
65 #[error("while parsing 'meta/contents'")]
66 Parse(#[source] MetaContentsError),
67}
68
69#[derive(Debug, Error)]
71#[allow(missing_docs)]
72pub enum LoadAbiRevisionError {
73 #[error("while opening '{}'", AbiRevision::PATH)]
74 Open(#[from] OpenError),
75
76 #[error("while reading '{}'", AbiRevision::PATH)]
77 Read(#[from] ReadError),
78
79 #[error("while parsing '{}'", AbiRevision::PATH)]
80 Parse(#[from] std::array::TryFromSliceError),
81}
82
83#[derive(Debug, Clone)]
85pub struct PackageDirectory {
86 proxy: fio::DirectoryProxy,
87}
88
89impl PackageDirectory {
90 pub fn from_proxy(proxy: fio::DirectoryProxy) -> Self {
92 Self { proxy }
93 }
94
95 pub fn create_request() -> Result<(Self, ServerEnd<fio::DirectoryMarker>), fidl::Error> {
98 let (proxy, request) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
99 Ok((Self::from_proxy(proxy), request))
100 }
101
102 #[cfg(target_os = "fuchsia")]
104 pub fn open_from_namespace() -> Result<Self, OpenError> {
105 let dir = fuchsia_fs::directory::open_in_namespace("/pkg", fio::PERM_READABLE)?;
106 Ok(Self::from_proxy(dir))
107 }
108
109 pub async fn close(self) -> Result<(), CloseError> {
111 fuchsia_fs::directory::close(self.proxy).await
112 }
113
114 pub fn reopen(&self, dir_request: ServerEnd<fio::DirectoryMarker>) -> Result<(), CloneError> {
116 fuchsia_fs::directory::clone_onto(&self.proxy, dir_request)
117 }
118
119 pub fn into_proxy(self) -> fio::DirectoryProxy {
121 self.proxy
122 }
123
124 async fn read_file_to_string(&self, path: &str) -> Result<String, ReadError> {
127 fuchsia_fs::directory::read_file_to_string(&self.proxy, path).await
128 }
129
130 pub async fn read_file(&self, path: &str) -> Result<Vec<u8>, ReadError> {
132 fuchsia_fs::directory::read_file(&self.proxy, path).await
133 }
134
135 pub async fn merkle_root(&self) -> Result<Hash, ReadHashError> {
137 let merkle = self.read_file_to_string("meta").await.map_err(ReadHashError::Read)?;
138 merkle.parse().map_err(ReadHashError::Parse)
139 }
140
141 pub async fn meta_contents(&self) -> Result<MetaContents, LoadMetaContentsError> {
143 let meta_contents =
144 self.read_file("meta/contents").await.map_err(LoadMetaContentsError::Read)?;
145 let meta_contents = MetaContents::deserialize(meta_contents.as_slice())
146 .map_err(LoadMetaContentsError::Parse)?;
147 Ok(meta_contents)
148 }
149
150 pub async fn meta_package(&self) -> Result<MetaPackage, LoadMetaPackageError> {
152 let meta_package =
153 self.read_file("meta/package").await.map_err(LoadMetaPackageError::Read)?;
154 let meta_package = MetaPackage::deserialize(meta_package.as_slice())
155 .map_err(LoadMetaPackageError::Parse)?;
156 Ok(meta_package)
157 }
158
159 pub async fn meta_subpackages(&self) -> Result<MetaSubpackages, LoadMetaSubpackagesError> {
162 match self.read_file(MetaSubpackages::PATH).await {
163 Ok(file) => Ok(MetaSubpackages::deserialize(file.as_slice())
164 .map_err(LoadMetaSubpackagesError::Parse)?),
165 Err(ReadError::Open(OpenError::OpenError(zx_status::Status::NOT_FOUND))) => {
166 Ok(MetaSubpackages::default())
167 }
168 Err(err) => Err(LoadMetaSubpackagesError::Read(err)),
169 }
170 }
171
172 pub async fn abi_revision(&self) -> Result<AbiRevision, LoadAbiRevisionError> {
174 let abi_revision_bytes = self.read_file(AbiRevision::PATH).await?;
175 Ok(AbiRevision::try_from(abi_revision_bytes.as_slice())?)
176 }
177
178 pub async fn blobs(&self) -> Result<impl Iterator<Item = Hash>, LoadMetaContentsError> {
181 Ok(self.meta_contents().await?.into_hashes_undeduplicated())
182 }
183}
184
185#[cfg(test)]
186#[cfg(target_os = "fuchsia")]
187mod tests {
188 use super::*;
189 use assert_matches::assert_matches;
190 use fidl::endpoints::Proxy;
191
192 #[fuchsia::test]
193 async fn open_close() {
194 let pkg = PackageDirectory::open_from_namespace().unwrap();
195 let () = pkg.close().await.unwrap();
196 }
197
198 #[fuchsia::test]
199 async fn reopen_is_new_connection() {
200 let pkg = PackageDirectory::open_from_namespace().unwrap();
201
202 let (proxy, server_end) = fidl::endpoints::create_proxy();
203 assert_matches!(pkg.reopen(server_end), Ok(()));
204 assert_matches!(PackageDirectory::from_proxy(proxy).close().await, Ok(()));
205
206 pkg.into_proxy().into_channel().expect("no other users of the wrapped channel");
207 }
208
209 #[fuchsia::test]
210 async fn merkle_root_is_pkg_meta() {
211 let pkg = PackageDirectory::open_from_namespace().unwrap();
212
213 let merkle: Hash = std::fs::read_to_string("/pkg/meta").unwrap().parse().unwrap();
214
215 assert_eq!(pkg.merkle_root().await.unwrap(), merkle);
216 }
217
218 #[fuchsia::test]
219 async fn list_blobs() {
220 let pkg = PackageDirectory::open_from_namespace().unwrap();
221
222 let blobs = pkg.blobs().await.unwrap().collect::<Vec<_>>();
224 assert!(!blobs.is_empty());
225
226 let duplicate_blob_merkle = fuchsia_merkle::root_from_slice("Hello World!");
228 assert_eq!(blobs.iter().filter(|hash| *hash == &duplicate_blob_merkle).count(), 2);
229 }
230
231 #[fuchsia::test]
232 async fn package_name_is_test_package_name() {
233 let pkg = PackageDirectory::open_from_namespace().unwrap();
234
235 assert_eq!(
236 pkg.meta_package().await.unwrap().into_path().to_string(),
237 "fuchsia-pkg-tests/0"
238 );
239 }
240
241 #[fuchsia::test]
242 async fn missing_subpackages_file_is_empty_subpackages() {
243 let pkg = PackageDirectory::open_from_namespace().unwrap();
244
245 assert_eq!(pkg.meta_subpackages().await.unwrap(), MetaSubpackages::default(),);
246 }
247
248 #[fuchsia::test]
249 async fn abi_revision_succeeds() {
250 let pkg = PackageDirectory::open_from_namespace().unwrap();
251
252 let _: AbiRevision = pkg.abi_revision().await.unwrap();
253 }
254}