1#[cfg(test)]
6use crate::MetaSubpackages;
7use crate::{MetaContents, MetaContentsError, MetaPackage, PackageManifest};
8use anyhow::Result;
9use fuchsia_merkle::Hash;
10use fuchsia_url::{PackageName, RelativePackageUrl};
11use std::collections::{BTreeMap, HashMap};
12#[cfg(test)]
13use std::io::{Read, Seek};
14use std::path::PathBuf;
15
16#[derive(Clone, Debug, PartialEq, Eq)]
17pub(crate) struct Package {
18 meta_contents: MetaContents,
19 meta_package: MetaPackage,
20 subpackages: Vec<SubpackageEntry>,
21 blobs: BTreeMap<String, BlobEntry>,
22}
23
24impl Package {
25 #[cfg(test)]
27 pub fn meta_contents(&self) -> MetaContents {
28 self.meta_contents.clone()
29 }
30
31 pub fn meta_package(&self) -> MetaPackage {
33 self.meta_package.clone()
34 }
35
36 #[cfg(test)]
38 pub fn meta_subpackages(&self) -> MetaSubpackages {
39 MetaSubpackages::from_iter(self.subpackages.iter().map(
40 |SubpackageEntry { name, merkle, package_manifest_path: _ }| (name.clone(), *merkle),
41 ))
42 }
43
44 pub fn subpackages(&self) -> Vec<SubpackageEntry> {
46 self.subpackages.clone()
47 }
48
49 pub fn blobs(&self) -> BTreeMap<String, BlobEntry> {
51 self.blobs.clone()
52 }
53
54 pub(crate) fn builder(name: PackageName) -> Builder {
56 Builder::new(name)
57 }
58
59 #[cfg(test)]
61 pub fn from_meta_far<R: Read + Seek>(
62 mut meta_far: R,
63 blobs: BTreeMap<String, BlobEntry>,
64 subpackages: Vec<SubpackageEntry>,
65 ) -> Result<Self> {
66 let mut meta_far = fuchsia_archive::Utf8Reader::new(&mut meta_far)?;
67 let meta_contents =
68 MetaContents::deserialize(meta_far.read_file(MetaContents::PATH)?.as_slice())?;
69 let meta_package =
70 MetaPackage::deserialize(meta_far.read_file(MetaPackage::PATH)?.as_slice())?;
71 Ok(Package { meta_contents, meta_package, subpackages, blobs })
72 }
73}
74
75pub(crate) struct Builder {
76 contents: HashMap<String, Hash>,
77 meta_package: MetaPackage,
78 subpackages: Vec<SubpackageEntry>,
79 blobs: BTreeMap<String, BlobEntry>,
80}
81
82impl Builder {
83 pub(crate) fn new(name: PackageName) -> Self {
84 Self {
85 contents: HashMap::new(),
86 meta_package: MetaPackage::from_name_and_variant_zero(name),
87 subpackages: Vec::new(),
88 blobs: BTreeMap::new(),
89 }
90 }
91
92 pub(crate) fn add_entry(
93 &mut self,
94 blob_path: String,
95 hash: Hash,
96 source_path: PathBuf,
97 size: u64,
98 ) {
99 if blob_path != PackageManifest::META_FAR_BLOB_PATH {
100 self.contents.insert(blob_path.clone(), hash);
101 }
102 self.blobs.insert(blob_path, BlobEntry { source_path, size, hash });
103 }
104
105 pub(crate) fn add_subpackage(
106 &mut self,
107 name: RelativePackageUrl,
108 merkle: Hash,
109 package_manifest_path: PathBuf,
110 ) {
111 self.subpackages.push(SubpackageEntry { name, merkle, package_manifest_path });
112 }
113
114 pub(crate) fn build(self) -> Result<Package, MetaContentsError> {
115 Ok(Package {
116 meta_contents: MetaContents::from_map(self.contents)?,
117 meta_package: self.meta_package,
118 subpackages: self.subpackages,
119 blobs: self.blobs,
120 })
121 }
122}
123
124#[derive(Clone, Debug, PartialEq, Eq)]
125pub(crate) struct BlobEntry {
126 source_path: PathBuf,
127 hash: Hash,
128 size: u64,
129}
130
131impl BlobEntry {
132 pub fn source_path(&self) -> PathBuf {
133 self.source_path.clone()
134 }
135
136 pub fn size(&self) -> u64 {
137 self.size
138 }
139
140 pub fn hash(&self) -> Hash {
141 self.hash
142 }
143}
144
145#[derive(Clone, Debug, PartialEq, Eq)]
146pub(crate) struct SubpackageEntry {
147 pub name: RelativePackageUrl,
148 pub merkle: Hash,
149 pub package_manifest_path: PathBuf,
150}
151
152#[cfg(test)]
153mod test_package {
154 use super::*;
155 use crate::build::{build_with_file_system, FileSystem};
156 use crate::PackageBuildManifest;
157
158 use maplit::{btreemap, hashmap};
159 use std::fs::File;
160 use std::io;
161 use std::str::FromStr;
162 use tempfile::tempdir;
163
164 fn zeros_hash() -> Hash {
165 "0000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()
166 }
167
168 fn ones_hash() -> Hash {
169 "1111111111111111111111111111111111111111111111111111111111111111".parse().unwrap()
170 }
171
172 #[test]
173 fn test_create_package() {
174 let meta_package = MetaPackage::from_name_and_variant_zero("package-name".parse().unwrap());
175
176 let subpackages = vec![
177 SubpackageEntry {
178 name: RelativePackageUrl::parse("a_0_subpackage").unwrap(),
179 merkle: zeros_hash(),
180 package_manifest_path: PathBuf::from("some/path/to/package_manifest.json"),
181 },
182 SubpackageEntry {
183 name: RelativePackageUrl::parse("other-1-subpackage").unwrap(),
184 merkle: ones_hash(),
185 package_manifest_path: PathBuf::from("another/path/to/package_manifest.json"),
186 },
187 ];
188
189 let meta_subpackages = MetaSubpackages::from_iter(hashmap! {
190 subpackages[0].name.clone() => subpackages[0].merkle,
191 subpackages[1].name.clone() => subpackages[1].merkle,
192 });
193
194 let map = hashmap! {
195 "bin/my_prog".to_string() =>
196 Hash::from_str(
197 "0000000000000000000000000000000000000000000000000000000000000000")
198 .unwrap(),
199 "lib/mylib.so".to_string() =>
200 Hash::from_str(
201 "1111111111111111111111111111111111111111111111111111111111111111")
202 .unwrap(),
203 };
204 let meta_contents = MetaContents::from_map(map).unwrap();
205 let blob_entry = BlobEntry {
206 source_path: PathBuf::from("src/bin/my_prog"),
207 size: 1,
208 hash: Hash::from_str(
209 "0000000000000000000000000000000000000000000000000000000000000000",
210 )
211 .unwrap(),
212 };
213 let blobs = btreemap! {
214 "bin/my_prog".to_string() => blob_entry.clone(),
215 "bin/my_prog2".to_string() => blob_entry,
216 };
217 let package = Package {
218 meta_contents: meta_contents.clone(),
219 meta_package: meta_package.clone(),
220 subpackages: subpackages.clone(),
221 blobs: blobs.clone(),
222 };
223 assert_eq!(meta_package, package.meta_package());
224 assert_eq!(meta_subpackages, package.meta_subpackages());
225 assert_eq!(subpackages, package.subpackages());
226 assert_eq!(meta_contents, package.meta_contents());
227 assert_eq!(blobs, package.blobs());
228 }
229
230 struct FakeFileSystem {
231 content_map: HashMap<String, Vec<u8>>,
232 }
233
234 impl<'a> FileSystem<'a> for FakeFileSystem {
235 type File = &'a [u8];
236 fn open(&'a self, path: &str) -> Result<Self::File, io::Error> {
237 Ok(self.content_map.get(path).unwrap().as_slice())
238 }
239 fn len(&self, path: &str) -> Result<u64, io::Error> {
240 Ok(self.content_map.get(path).unwrap().len() as u64)
241 }
242 fn read(&self, path: &str) -> Result<Vec<u8>, io::Error> {
243 Ok(self.content_map.get(path).unwrap().clone())
244 }
245 }
246
247 #[test]
248 fn test_from_meta_far_valid_meta_far() {
249 let outdir = tempdir().unwrap();
250 let meta_far_path = outdir.path().join("base.far");
251
252 let creation_manifest = PackageBuildManifest::from_external_and_far_contents(
253 btreemap! {
254 "lib/mylib.so".to_string() => "host/mylib.so".to_string()
255 },
256 btreemap! {
257 "meta/my_component.cml".to_string() => "host/my_component.cml".to_string(),
258 "meta/package".to_string() => "host/meta/package".to_string()
259 },
260 )
261 .unwrap();
262 let component_manifest_contents = "my_component.cml contents";
263 let mut meta_package_json_bytes = vec![];
264 let meta_package =
265 MetaPackage::from_name_and_variant_zero("my-package-name".parse().unwrap());
266 meta_package.serialize(&mut meta_package_json_bytes).unwrap();
267
268 let subpackages = vec![
269 SubpackageEntry {
270 name: RelativePackageUrl::parse("a_0_subpackage").unwrap(),
271 merkle: zeros_hash(),
272 package_manifest_path: PathBuf::from("some/path/to/package_manifest.json"),
273 },
274 SubpackageEntry {
275 name: RelativePackageUrl::parse("other-1-subpackage").unwrap(),
276 merkle: ones_hash(),
277 package_manifest_path: PathBuf::from("another/path/to/package_manifest.json"),
278 },
279 ];
280
281 let meta_subpackages = MetaSubpackages::from_iter(hashmap! {
282 subpackages[0].name.clone() => subpackages[0].merkle,
283 subpackages[1].name.clone() => subpackages[1].merkle,
284 });
285
286 let mut meta_subpackages_json_bytes = vec![];
287 meta_subpackages.serialize(&mut meta_subpackages_json_bytes).unwrap();
288
289 let file_system = FakeFileSystem {
290 content_map: hashmap! {
291 "host/mylib.so".to_string() => "mylib.so contents".as_bytes().to_vec(),
292 "host/my_component.cml".to_string() => component_manifest_contents.as_bytes().to_vec(),
293 format!("host/{}", MetaPackage::PATH) => meta_package_json_bytes,
294 format!("host/{}", MetaSubpackages::PATH) => meta_subpackages_json_bytes,
295 },
296 };
297
298 build_with_file_system(
299 &creation_manifest,
300 &meta_far_path,
301 "my-package-name",
302 subpackages.clone(),
303 None,
304 &file_system,
305 )
306 .unwrap();
307
308 let blob_entry = BlobEntry {
309 source_path: PathBuf::from("src/bin/my_prog"),
310 size: 1,
311 hash: Hash::from_str(
312 "0000000000000000000000000000000000000000000000000000000000000000",
313 )
314 .unwrap(),
315 };
316 let blobs = btreemap! {
317 "bin/my_prog".to_string() => blob_entry.clone(),
318 "bin/my_prog2".to_string() => blob_entry,
319 };
320 let package =
321 Package::from_meta_far(File::open(&meta_far_path).unwrap(), blobs.clone(), subpackages)
322 .unwrap();
323 assert_eq!(blobs, package.blobs());
324 assert_eq!(
325 &"my-package-name".parse::<PackageName>().unwrap(),
326 package.meta_package().name()
327 );
328 assert_eq!(
329 package.meta_subpackages().subpackages().get(&"a_0_subpackage".try_into().unwrap()),
330 Some(&zeros_hash())
331 );
332 assert_eq!(
333 package.meta_subpackages().subpackages().get(&"other-1-subpackage".try_into().unwrap()),
334 Some(&ones_hash())
335 );
336 }
337
338 #[test]
339 fn test_from_meta_far_empty_meta_far() {
340 let dir = tempdir().unwrap();
341 let file_path = dir.path().join("meta.far");
342 File::create(&file_path).unwrap();
343 let file = File::open(&file_path).unwrap();
344 let blob_entry = BlobEntry {
345 source_path: PathBuf::from("src/bin/my_prog"),
346 size: 1,
347 hash: Hash::from_str(
348 "0000000000000000000000000000000000000000000000000000000000000000",
349 )
350 .unwrap(),
351 };
352 let blobs = btreemap! {
353 "bin/my_prog".to_string() => blob_entry.clone(),
354 "bin/my_prog2".to_string() => blob_entry,
355 };
356 let package = Package::from_meta_far(file, blobs, Vec::new());
357 assert!(package.is_err());
358 }
359}