1use {
12 crate::{
13 resource::Resource,
14 serialization::{BlobInteriorPadding, BlobSectionField, ResourceField, HEADER_V3},
15 },
16 byteorder::{LittleEndian, ReadBytesExt},
17 std::{borrow::Cow, collections::HashMap, io::Cursor, path::Path},
18};
19
20#[cfg(unix)]
21use std::{ffi::OsStr, os::unix::ffi::OsStrExt};
22#[cfg(windows)]
23use {std::ffi::OsString, std::os::windows::ffi::OsStringExt, std::path::PathBuf};
24
25#[derive(Debug)]
27struct BlobSection {
28 resource_field: u8,
29 raw_payload_length: usize,
30 interior_padding: Option<BlobInteriorPadding>,
31}
32
33#[derive(Clone, Copy, Debug)]
35struct BlobSectionReadState {
36 offset: usize,
37 interior_padding: BlobInteriorPadding,
38}
39
40pub struct ResourceParserIterator<'a> {
45 done: bool,
46 data: &'a [u8],
47 reader: Cursor<&'a [u8]>,
48 blob_sections: [Option<BlobSectionReadState>; 256],
49 claimed_resources_count: usize,
50 read_resources_count: usize,
51}
52
53impl<'a> ResourceParserIterator<'a> {
54 pub fn expected_resources_count(&self) -> usize {
56 self.claimed_resources_count
57 }
58
59 fn resolve_blob_data(&mut self, resource_field: ResourceField, length: usize) -> &'a [u8] {
65 let mut state = self.blob_sections[resource_field as usize]
66 .as_mut()
67 .expect("blob state not found");
68
69 let blob = &self.data[state.offset..state.offset + length];
70
71 let increment = match &state.interior_padding {
72 BlobInteriorPadding::None => length,
73 BlobInteriorPadding::Null => length + 1,
74 };
75
76 state.offset += increment;
77
78 blob
79 }
80
81 #[cfg(unix)]
82 fn resolve_path(&mut self, resource_field: ResourceField, length: usize) -> Cow<'a, Path> {
83 let path_str = OsStr::from_bytes(self.resolve_blob_data(resource_field, length));
84 Cow::Borrowed(Path::new(path_str))
85 }
86
87 #[cfg(windows)]
88 fn resolve_path(&mut self, resource_field: ResourceField, length: usize) -> Cow<'a, Path> {
89 let raw = self.resolve_blob_data(resource_field, length);
90 let raw = unsafe { std::slice::from_raw_parts(raw.as_ptr() as *const u16, raw.len() / 2) };
91
92 let path_string = OsString::from_wide(raw);
95
96 Cow::Owned(PathBuf::from(path_string))
97 }
98
99 fn parse_next(&mut self) -> Result<Option<Resource<'a, u8>>, &'static str> {
100 let mut current_resource = Resource::default();
101 let mut current_resource_name = None;
102
103 loop {
104 let field_type = self
105 .reader
106 .read_u8()
107 .map_err(|_| "failed reading field type")?;
108
109 let field_type = ResourceField::try_from(field_type)?;
110
111 match field_type {
112 ResourceField::EndOfIndex => {
113 self.done = true;
114
115 if self.read_resources_count != self.claimed_resources_count {
116 return Err("mismatch between advertised index count and actual");
117 }
118
119 return Ok(None);
120 }
121 ResourceField::StartOfEntry => {
122 self.read_resources_count += 1;
123 current_resource = Resource::default();
124 current_resource_name = None;
125 }
126 ResourceField::EndOfEntry => {
127 let res = if current_resource_name.is_some() {
128 Ok(Some(current_resource))
129 } else {
130 Err("resource name field is required")
131 };
132
133 return res;
134 }
135 ResourceField::Name => {
136 let l = self
137 .reader
138 .read_u16::<LittleEndian>()
139 .map_err(|_| "failed reading resource name length")?
140 as usize;
141
142 let name = unsafe {
143 std::str::from_utf8_unchecked(self.resolve_blob_data(field_type, l))
144 };
145
146 current_resource_name = Some(name);
147 current_resource.name = Cow::Borrowed(name);
148 }
149 ResourceField::IsPythonPackage => {
150 current_resource.is_python_package = true;
151 }
152 ResourceField::IsPythonNamespacePackage => {
153 current_resource.is_python_namespace_package = true;
154 }
155 ResourceField::InMemorySource => {
156 let l = self
157 .reader
158 .read_u32::<LittleEndian>()
159 .map_err(|_| "failed reading source length")?
160 as usize;
161
162 current_resource.in_memory_source =
163 Some(Cow::Borrowed(self.resolve_blob_data(field_type, l)));
164 }
165 ResourceField::InMemoryBytecode => {
166 let l = self
167 .reader
168 .read_u32::<LittleEndian>()
169 .map_err(|_| "failed reading bytecode length")?
170 as usize;
171
172 current_resource.in_memory_bytecode =
173 Some(Cow::Borrowed(self.resolve_blob_data(field_type, l)));
174 }
175 ResourceField::InMemoryBytecodeOpt1 => {
176 let l = self
177 .reader
178 .read_u32::<LittleEndian>()
179 .map_err(|_| "failed reading bytecode length")?
180 as usize;
181
182 current_resource.in_memory_bytecode_opt1 =
183 Some(Cow::Borrowed(self.resolve_blob_data(field_type, l)));
184 }
185 ResourceField::InMemoryBytecodeOpt2 => {
186 let l = self
187 .reader
188 .read_u32::<LittleEndian>()
189 .map_err(|_| "failed reading bytecode length")?
190 as usize;
191
192 current_resource.in_memory_bytecode_opt2 =
193 Some(Cow::Borrowed(self.resolve_blob_data(field_type, l)));
194 }
195 ResourceField::InMemoryExtensionModuleSharedLibrary => {
196 let l = self
197 .reader
198 .read_u32::<LittleEndian>()
199 .map_err(|_| "failed reading extension module length")?
200 as usize;
201
202 current_resource.in_memory_extension_module_shared_library =
203 Some(Cow::Borrowed(self.resolve_blob_data(field_type, l)));
204 }
205
206 ResourceField::InMemoryResourcesData => {
207 let resource_count = self
208 .reader
209 .read_u32::<LittleEndian>()
210 .map_err(|_| "failed reading resources length")?
211 as usize;
212
213 let mut resources = HashMap::with_capacity(resource_count);
214
215 for _ in 0..resource_count {
216 let resource_name_length = self
217 .reader
218 .read_u16::<LittleEndian>()
219 .map_err(|_| "failed reading resource name")?
220 as usize;
221
222 let resource_name = unsafe {
223 std::str::from_utf8_unchecked(
224 self.resolve_blob_data(field_type, resource_name_length),
225 )
226 };
227
228 let resource_length = self
229 .reader
230 .read_u64::<LittleEndian>()
231 .map_err(|_| "failed reading resource length")?
232 as usize;
233
234 let resource_data = self.resolve_blob_data(field_type, resource_length);
235
236 resources
237 .insert(Cow::Borrowed(resource_name), Cow::Borrowed(resource_data));
238 }
239
240 current_resource.in_memory_package_resources = Some(resources);
241 }
242
243 ResourceField::InMemoryDistributionResource => {
244 let resource_count = self
245 .reader
246 .read_u32::<LittleEndian>()
247 .map_err(|_| "failed reading package distribution length")?
248 as usize;
249
250 let mut resources = HashMap::with_capacity(resource_count);
251
252 for _ in 0..resource_count {
253 let name_length = self
254 .reader
255 .read_u16::<LittleEndian>()
256 .map_err(|_| "failed reading distribution metadata name")?
257 as usize;
258
259 let name = unsafe {
260 std::str::from_utf8_unchecked(
261 self.resolve_blob_data(field_type, name_length),
262 )
263 };
264
265 let resource_length =
266 self.reader.read_u64::<LittleEndian>().map_err(|_| {
267 "failed reading package distribution resource length"
268 })? as usize;
269
270 let resource_data = self.resolve_blob_data(field_type, resource_length);
271
272 resources.insert(Cow::Borrowed(name), Cow::Borrowed(resource_data));
273 }
274
275 current_resource.in_memory_distribution_resources = Some(resources);
276 }
277
278 ResourceField::InMemorySharedLibrary => {
279 let l = self
280 .reader
281 .read_u64::<LittleEndian>()
282 .map_err(|_| "failed reading in-memory shared library length")?
283 as usize;
284
285 current_resource.in_memory_shared_library =
286 Some(Cow::Borrowed(self.resolve_blob_data(field_type, l)));
287 }
288
289 ResourceField::SharedLibraryDependencyNames => {
290 let names_count = self
291 .reader
292 .read_u16::<LittleEndian>()
293 .map_err(|_| "failed reading shared library dependency names length")?
294 as usize;
295
296 let mut names = Vec::new();
297
298 for _ in 0..names_count {
299 let name_length =
300 self.reader.read_u16::<LittleEndian>().map_err(|_| {
301 "failed reading shared library dependency name length"
302 })? as usize;
303
304 let name = unsafe {
305 std::str::from_utf8_unchecked(
306 self.resolve_blob_data(field_type, name_length),
307 )
308 };
309
310 names.push(Cow::Borrowed(name));
311 }
312
313 current_resource.shared_library_dependency_names = Some(names);
314 }
315
316 ResourceField::RelativeFilesystemModuleSource => {
317 let path_length = self
318 .reader
319 .read_u32::<LittleEndian>()
320 .map_err(|_| "failed reading Python module relative path length")?
321 as usize;
322
323 let path = self.resolve_path(field_type, path_length);
324
325 current_resource.relative_path_module_source = Some(path);
326 }
327
328 ResourceField::RelativeFilesystemModuleBytecode => {
329 let path_length =
330 self.reader.read_u32::<LittleEndian>().map_err(|_| {
331 "failed reading Python module bytecode relative path length"
332 })? as usize;
333
334 let path = self.resolve_path(field_type, path_length);
335
336 current_resource.relative_path_module_bytecode = Some(path);
337 }
338
339 ResourceField::RelativeFilesystemModuleBytecodeOpt1 => {
340 let path_length = self.reader.read_u32::<LittleEndian>().map_err(|_| {
341 "failed reading Python module bytecode opt 1 relative path length"
342 })? as usize;
343
344 let path = self.resolve_path(field_type, path_length);
345
346 current_resource.relative_path_module_bytecode_opt1 = Some(path);
347 }
348
349 ResourceField::RelativeFilesystemModuleBytecodeOpt2 => {
350 let path_length = self.reader.read_u32::<LittleEndian>().map_err(|_| {
351 "failed reading Python module bytecode opt 2 relative path length"
352 })? as usize;
353
354 let path = self.resolve_path(field_type, path_length);
355
356 current_resource.relative_path_module_bytecode_opt2 = Some(path);
357 }
358
359 ResourceField::RelativeFilesystemExtensionModuleSharedLibrary => {
360 let path_length = self.reader.read_u32::<LittleEndian>().map_err(|_| {
361 "failed reading Python extension module shared library relative path length"
362 })? as usize;
363
364 let path = self.resolve_path(field_type, path_length);
365
366 current_resource.relative_path_extension_module_shared_library = Some(path);
367 }
368
369 ResourceField::RelativeFilesystemPackageResources => {
370 let resource_count =
371 self.reader.read_u32::<LittleEndian>().map_err(|_| {
372 "failed reading package resources relative path item count"
373 })? as usize;
374
375 let mut resources = HashMap::with_capacity(resource_count);
376
377 for _ in 0..resource_count {
378 let resource_name_length = self
379 .reader
380 .read_u16::<LittleEndian>()
381 .map_err(|_| "failed reading resource name")?
382 as usize;
383
384 let resource_name = unsafe {
385 std::str::from_utf8_unchecked(
386 self.resolve_blob_data(field_type, resource_name_length),
387 )
388 };
389
390 let path_length = self
391 .reader
392 .read_u32::<LittleEndian>()
393 .map_err(|_| "failed reading resource path length")?
394 as usize;
395
396 let path = self.resolve_path(field_type, path_length);
397
398 resources.insert(Cow::Borrowed(resource_name), path);
399 }
400
401 current_resource.relative_path_package_resources = Some(resources);
402 }
403
404 ResourceField::RelativeFilesystemDistributionResource => {
405 let resource_count = self.reader.read_u32::<LittleEndian>().map_err(|_| {
406 "failed reading package distribution relative path item count"
407 })? as usize;
408
409 let mut resources = HashMap::with_capacity(resource_count);
410
411 for _ in 0..resource_count {
412 let name_length = self
413 .reader
414 .read_u16::<LittleEndian>()
415 .map_err(|_| "failed reading package distribution metadata name")?
416 as usize;
417
418 let name = unsafe {
419 std::str::from_utf8_unchecked(
420 self.resolve_blob_data(field_type, name_length),
421 )
422 };
423
424 let path_length = self
425 .reader
426 .read_u32::<LittleEndian>()
427 .map_err(|_| "failed reading package distribution path length")?
428 as usize;
429
430 let path = self.resolve_path(field_type, path_length);
431
432 resources.insert(Cow::Borrowed(name), path);
433 }
434
435 current_resource.relative_path_distribution_resources = Some(resources);
436 }
437
438 ResourceField::IsPythonModule => {
439 current_resource.is_python_module = true;
440 }
441
442 ResourceField::IsPythonBuiltinExtensionModule => {
443 current_resource.is_python_builtin_extension_module = true;
444 }
445
446 ResourceField::IsPythonFrozenModule => {
447 current_resource.is_python_frozen_module = true;
448 }
449
450 ResourceField::IsPythonExtensionModule => {
451 current_resource.is_python_extension_module = true;
452 }
453
454 ResourceField::IsSharedLibrary => {
455 current_resource.is_shared_library = true;
456 }
457
458 ResourceField::IsUtf8FilenameData => {
459 current_resource.is_utf8_filename_data = true;
460 }
461
462 ResourceField::FileExecutable => {
463 current_resource.file_executable = true;
464 }
465
466 ResourceField::FileDataEmbedded => {
467 let l = self
468 .reader
469 .read_u64::<LittleEndian>()
470 .map_err(|_| "failed reading embedded file data length")?
471 as usize;
472
473 current_resource.file_data_embedded =
474 Some(Cow::Borrowed(self.resolve_blob_data(field_type, l)));
475 }
476
477 ResourceField::FileDataUtf8RelativePath => {
478 let l = self
479 .reader
480 .read_u32::<LittleEndian>()
481 .map_err(|_| "failed reading file data relative path length")?
482 as usize;
483
484 current_resource.file_data_utf8_relative_path = Some(Cow::Borrowed(unsafe {
485 std::str::from_utf8_unchecked(self.resolve_blob_data(field_type, l))
486 }));
487 }
488 }
489 }
490 }
491}
492
493impl<'a> Iterator for ResourceParserIterator<'a> {
494 type Item = Result<Resource<'a, u8>, &'static str>;
495
496 fn next(&mut self) -> Option<Self::Item> {
497 if self.done {
498 return None;
499 }
500
501 match self.parse_next() {
502 Ok(res) => res.map(Ok),
503 Err(e) => Some(Err(e)),
504 }
505 }
506}
507
508pub fn load_resources<'a>(data: &'a [u8]) -> Result<ResourceParserIterator<'a>, &'static str> {
517 if data.len() < HEADER_V3.len() {
518 return Err("error reading 8 byte header");
519 }
520
521 let header = &data[0..8];
522
523 if header == HEADER_V3 {
524 load_resources_v3(&data[8..])
525 } else {
526 Err("unrecognized file format")
527 }
528}
529
530fn load_resources_v3<'a>(data: &'a [u8]) -> Result<ResourceParserIterator<'a>, &'static str> {
531 let mut reader = Cursor::new(data);
532
533 let blob_section_count = reader
534 .read_u8()
535 .map_err(|_| "failed reading blob section count")?;
536 let blob_index_length = reader
537 .read_u32::<LittleEndian>()
538 .map_err(|_| "failed reading blob index length")? as usize;
539 let resources_count = reader
540 .read_u32::<LittleEndian>()
541 .map_err(|_| "failed reading resources count")? as usize;
542 let resources_index_length = reader
543 .read_u32::<LittleEndian>()
544 .map_err(|_| "failed reading resources index length")?
545 as usize;
546
547 let mut current_blob_field = None;
548 let mut current_blob_raw_payload_length = None;
549 let mut current_blob_interior_padding = None;
550 let mut blob_entry_count = 0;
551 let mut blob_sections = Vec::with_capacity(blob_section_count as usize);
552
553 if blob_section_count != 0 || blob_index_length != 0 {
554 loop {
555 let field_type = reader
556 .read_u8()
557 .map_err(|_| "failed reading blob section field type")?;
558
559 let field_type = BlobSectionField::try_from(field_type)?;
560
561 match field_type {
562 BlobSectionField::EndOfIndex => break,
563 BlobSectionField::StartOfEntry => {
564 blob_entry_count += 1;
565 current_blob_field = None;
566 current_blob_raw_payload_length = None;
567 current_blob_interior_padding = None;
568 }
569 BlobSectionField::EndOfEntry => {
570 if current_blob_field.is_none() {
571 return Err("blob resource field is required");
572 }
573 if current_blob_raw_payload_length.is_none() {
574 return Err("blob raw payload length is required");
575 }
576
577 blob_sections.push(BlobSection {
578 resource_field: current_blob_field.unwrap(),
579 raw_payload_length: current_blob_raw_payload_length.unwrap(),
580 interior_padding: current_blob_interior_padding,
581 });
582
583 current_blob_field = None;
584 current_blob_raw_payload_length = None;
585 current_blob_interior_padding = None;
586 }
587 BlobSectionField::ResourceFieldType => {
588 let field = reader
589 .read_u8()
590 .map_err(|_| "failed reading blob resource field value")?;
591 current_blob_field = Some(field);
592 }
593 BlobSectionField::RawPayloadLength => {
594 let l = reader
595 .read_u64::<LittleEndian>()
596 .map_err(|_| "failed reading raw payload length")?;
597 current_blob_raw_payload_length = Some(l as usize);
598 }
599 BlobSectionField::InteriorPadding => {
600 let padding = reader
601 .read_u8()
602 .map_err(|_| "failed reading interior padding field value")?;
603
604 current_blob_interior_padding = Some(match padding {
605 0x01 => BlobInteriorPadding::None,
606 0x02 => BlobInteriorPadding::Null,
607 _ => return Err("invalid value for interior padding field"),
608 });
609 }
610 }
611 }
612 }
613
614 if blob_entry_count != blob_section_count {
615 return Err("mismatch between blob sections count");
616 }
617
618 let mut blob_offsets: [Option<BlobSectionReadState>; 256] = [None; 256];
620
621 let blob_start_offset: usize =
623 1 + 4 + 4 + 4
625 + blob_index_length
626 + resources_index_length
627 ;
628 let mut current_blob_offset = 0;
630
631 for section in &blob_sections {
632 let section_start_offset = blob_start_offset + current_blob_offset;
633 blob_offsets[section.resource_field as usize] = Some(BlobSectionReadState {
634 offset: section_start_offset,
635 interior_padding: match section.interior_padding {
636 Some(padding) => padding,
637 None => BlobInteriorPadding::None,
638 },
639 });
640 current_blob_offset += section.raw_payload_length;
641 }
642
643 Ok(ResourceParserIterator {
644 done: resources_index_length == 0 || resources_count == 0,
645 data,
646 reader,
647 blob_sections: blob_offsets,
648 claimed_resources_count: resources_count,
649 read_resources_count: 0,
650 })
651}
652
653#[cfg(test)]
654mod tests {
655 use {
656 super::*,
657 crate::{
658 resource::Resource, serialization::BlobInteriorPadding,
659 writer::write_packed_resources_v3,
660 },
661 };
662
663 #[test]
664 fn test_too_short_header() {
665 let data = b"foo";
666
667 let res = load_resources(data);
668 assert_eq!(res.err(), Some("error reading 8 byte header"));
669 }
670
671 #[test]
672 fn test_unrecognized_header() {
673 let data = b"pyembed\x00";
674 let res = load_resources(data);
675 assert_eq!(res.err(), Some("unrecognized file format"));
676
677 let data = b"pyembed\x04";
678 let res = load_resources(data);
679 assert_eq!(res.err(), Some("unrecognized file format"));
680 }
681
682 #[test]
683 fn test_no_indices() {
684 let data = b"pyembed\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
685 load_resources(data).unwrap();
686 }
687
688 #[test]
689 fn test_no_blob_index() {
690 let data = b"pyembed\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00";
691 load_resources(data).unwrap();
692 }
693
694 #[test]
695 fn test_no_resource_index() {
696 let data = b"pyembed\x03\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
697 load_resources(data).unwrap();
698 }
699
700 #[test]
701 fn test_empty_indices() {
702 let data = b"pyembed\x03\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00";
703 load_resources(data).unwrap();
704 }
705
706 #[test]
707 fn test_index_count_mismatch() {
708 let data = b"pyembed\x03\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00";
709 let mut res = load_resources(data).unwrap();
710 assert_eq!(
711 res.next(),
712 Some(Err("mismatch between advertised index count and actual"))
713 );
714 assert_eq!(res.next(), None);
715 }
716
717 #[test]
718 fn test_missing_resource_name() {
719 let data =
720 b"pyembed\x03\x00\x01\x00\x00\x00\x01\x00\x00\x00\x03\x00\x00\x00\x00\x01\xff\x00";
721 let mut res = load_resources(data).unwrap();
722 assert_eq!(res.next(), Some(Err("resource name field is required")));
723 assert_eq!(res.next(), None);
724 }
725
726 #[test]
727 fn test_just_resource_name() {
728 let resource = Resource {
729 name: Cow::from("foo"),
730 ..Resource::default()
731 };
732
733 let mut data = Vec::new();
734 write_packed_resources_v3(&[resource], &mut data, None).unwrap();
735
736 let resources = load_resources(&data)
737 .unwrap()
738 .collect::<Result<Vec<Resource<u8>>, &'static str>>()
739 .unwrap();
740
741 assert_eq!(resources.len(), 1);
742
743 let entry = &resources[0];
744 assert_eq!(
745 entry,
746 &Resource {
747 name: Cow::from("foo"),
748 ..Resource::default()
749 }
750 );
751 }
752
753 #[test]
754 fn test_multiple_resources_just_names() {
755 let resource1 = Resource {
756 name: Cow::from("foo"),
757 ..Resource::default()
758 };
759
760 let resource2 = Resource {
761 name: Cow::from("module2"),
762 ..Resource::default()
763 };
764
765 let mut data = Vec::new();
766 write_packed_resources_v3(&[resource1, resource2], &mut data, None).unwrap();
767 let resources = load_resources(&data)
768 .unwrap()
769 .collect::<Result<Vec<Resource<u8>>, &'static str>>()
770 .unwrap();
771
772 assert_eq!(resources.len(), 2);
773
774 let entry = &resources[0];
775 assert_eq!(
776 entry,
777 &Resource {
778 name: Cow::Borrowed("foo"),
779 ..Resource::default()
780 }
781 );
782
783 let entry = &resources[1];
784 assert_eq!(
785 entry,
786 &Resource {
787 name: Cow::Borrowed("module2"),
788 ..Resource::default()
789 }
790 );
791 }
792
793 #[test]
795 fn test_multiple_resources_just_names_null_padding() {
796 let resource1 = Resource {
797 name: Cow::from("foo"),
798 ..Resource::default()
799 };
800
801 let resource2 = Resource {
802 name: Cow::from("module2"),
803 ..Resource::default()
804 };
805
806 let mut data = Vec::new();
807 write_packed_resources_v3(
808 &[resource1, resource2],
809 &mut data,
810 Some(BlobInteriorPadding::Null),
811 )
812 .unwrap();
813 let resources = load_resources(&data)
814 .unwrap()
815 .collect::<Result<Vec<Resource<u8>>, &'static str>>()
816 .unwrap();
817
818 assert_eq!(resources.len(), 2);
819
820 let entry = &resources[0];
821 assert_eq!(
822 entry,
823 &Resource {
824 name: Cow::Borrowed("foo"),
825 ..Resource::default()
826 }
827 );
828
829 let entry = &resources[1];
830 assert_eq!(
831 entry,
832 &Resource {
833 name: Cow::Borrowed("module2"),
834 ..Resource::default()
835 }
836 );
837 }
838
839 #[test]
840 fn test_in_memory_source() {
841 let resource = Resource {
842 name: Cow::from("foo"),
843 in_memory_source: Some(Cow::from(b"source".to_vec())),
844 ..Resource::default()
845 };
846
847 let mut data = Vec::new();
848 write_packed_resources_v3(&[resource], &mut data, None).unwrap();
849 let resources = load_resources(&data)
850 .unwrap()
851 .collect::<Result<Vec<Resource<u8>>, &'static str>>()
852 .unwrap();
853
854 assert_eq!(resources.len(), 1);
855
856 let entry = &resources[0];
857
858 assert_eq!(entry.in_memory_source.as_ref().unwrap().as_ref(), b"source");
859
860 assert_eq!(
861 entry,
862 &Resource {
863 name: Cow::Borrowed("foo"),
864 in_memory_source: Some(Cow::Borrowed(&data[data.len() - 6..data.len()])),
865 ..Resource::default()
866 }
867 );
868 }
869
870 #[test]
871 fn test_in_memory_bytecode() {
872 let resource = Resource {
873 name: Cow::from("foo"),
874 in_memory_bytecode: Some(Cow::from(b"bytecode".to_vec())),
875 ..Resource::default()
876 };
877
878 let mut data = Vec::new();
879 write_packed_resources_v3(&[resource], &mut data, None).unwrap();
880 let resources = load_resources(&data)
881 .unwrap()
882 .collect::<Result<Vec<Resource<u8>>, &'static str>>()
883 .unwrap();
884
885 assert_eq!(resources.len(), 1);
886
887 let entry = &resources[0];
888
889 assert_eq!(
890 entry.in_memory_bytecode.as_ref().unwrap().as_ref(),
891 b"bytecode"
892 );
893
894 assert_eq!(
895 entry,
896 &Resource {
897 name: Cow::Borrowed("foo"),
898 in_memory_bytecode: Some(Cow::Borrowed(&data[data.len() - 8..data.len()])),
899 ..Resource::default()
900 }
901 );
902 }
903
904 #[test]
905 fn test_in_memory_bytecode_opt1() {
906 let resource = Resource {
907 name: Cow::from("foo"),
908 in_memory_bytecode_opt1: Some(Cow::from(b"bytecode".to_vec())),
909 ..Resource::default()
910 };
911
912 let mut data = Vec::new();
913 write_packed_resources_v3(&[resource], &mut data, None).unwrap();
914 let resources = load_resources(&data)
915 .unwrap()
916 .collect::<Result<Vec<Resource<u8>>, &'static str>>()
917 .unwrap();
918
919 assert_eq!(resources.len(), 1);
920
921 let entry = &resources[0];
922
923 assert_eq!(
924 entry.in_memory_bytecode_opt1.as_ref().unwrap().as_ref(),
925 b"bytecode"
926 );
927
928 assert_eq!(
929 entry,
930 &Resource {
931 name: Cow::Borrowed("foo"),
932 in_memory_bytecode_opt1: Some(Cow::Borrowed(&data[data.len() - 8..data.len()])),
933 ..Resource::default()
934 }
935 );
936 }
937
938 #[test]
939 fn test_in_memory_bytecode_opt2() {
940 let resource = Resource {
941 name: Cow::from("foo"),
942 in_memory_bytecode_opt2: Some(Cow::from(b"bytecode".to_vec())),
943 ..Resource::default()
944 };
945
946 let mut data = Vec::new();
947 write_packed_resources_v3(&[resource], &mut data, None).unwrap();
948 let resources = load_resources(&data)
949 .unwrap()
950 .collect::<Result<Vec<Resource<u8>>, &'static str>>()
951 .unwrap();
952
953 assert_eq!(resources.len(), 1);
954
955 let entry = &resources[0];
956
957 assert_eq!(
958 entry.in_memory_bytecode_opt2.as_ref().unwrap().as_ref(),
959 b"bytecode"
960 );
961
962 assert_eq!(
963 entry,
964 &Resource {
965 name: Cow::Borrowed("foo"),
966 in_memory_bytecode_opt2: Some(Cow::Borrowed(&data[data.len() - 8..data.len()])),
967 ..Resource::default()
968 }
969 );
970 }
971
972 #[test]
973 fn test_in_memory_extension_module_shared_library() {
974 let resource = Resource {
975 name: Cow::from("foo"),
976 in_memory_extension_module_shared_library: Some(Cow::from(b"em".to_vec())),
977 ..Resource::default()
978 };
979
980 let mut data = Vec::new();
981 write_packed_resources_v3(&[resource], &mut data, None).unwrap();
982 let resources = load_resources(&data)
983 .unwrap()
984 .collect::<Result<Vec<Resource<u8>>, &'static str>>()
985 .unwrap();
986
987 assert_eq!(resources.len(), 1);
988
989 let entry = &resources[0];
990
991 assert_eq!(
992 entry
993 .in_memory_extension_module_shared_library
994 .as_ref()
995 .unwrap()
996 .as_ref(),
997 b"em"
998 );
999
1000 assert_eq!(
1001 entry,
1002 &Resource {
1003 name: Cow::Borrowed("foo"),
1004 in_memory_extension_module_shared_library: Some(Cow::Borrowed(
1005 &data[data.len() - 2..data.len()]
1006 )),
1007 ..Resource::default()
1008 }
1009 );
1010 }
1011
1012 #[test]
1013 fn test_in_memory_package_resources() {
1014 let mut resources = HashMap::new();
1015 resources.insert(Cow::from("foo"), Cow::from(b"foovalue".to_vec()));
1016 resources.insert(Cow::from("another"), Cow::from(b"value2".to_vec()));
1017
1018 let resource = Resource {
1019 name: Cow::from("foo"),
1020 in_memory_package_resources: Some(resources),
1021 ..Resource::default()
1022 };
1023
1024 let mut data = Vec::new();
1025 write_packed_resources_v3(&[resource], &mut data, None).unwrap();
1026 let resources = load_resources(&data)
1027 .unwrap()
1028 .collect::<Result<Vec<Resource<u8>>, &'static str>>()
1029 .unwrap();
1030
1031 assert_eq!(resources.len(), 1);
1032
1033 let entry = &resources[0];
1034
1035 let resources = entry.in_memory_package_resources.as_ref().unwrap();
1036 assert_eq!(resources.len(), 2);
1037 assert_eq!(resources.get("foo").unwrap().as_ref(), b"foovalue");
1038 assert_eq!(resources.get("another").unwrap().as_ref(), b"value2");
1039 }
1040
1041 #[test]
1042 fn test_in_memory_package_distribution() {
1043 let mut resources = HashMap::new();
1044 resources.insert(Cow::from("foo"), Cow::from(b"foovalue".to_vec()));
1045 resources.insert(Cow::from("another"), Cow::from(b"value2".to_vec()));
1046
1047 let resource = Resource {
1048 name: Cow::from("foo"),
1049 in_memory_distribution_resources: Some(resources),
1050 ..Resource::default()
1051 };
1052
1053 let mut data = Vec::new();
1054 write_packed_resources_v3(&[resource], &mut data, None).unwrap();
1055 let resources = load_resources(&data)
1056 .unwrap()
1057 .collect::<Result<Vec<Resource<u8>>, &'static str>>()
1058 .unwrap();
1059
1060 assert_eq!(resources.len(), 1);
1061
1062 let entry = &resources[0];
1063
1064 let resources = entry.in_memory_distribution_resources.as_ref().unwrap();
1065 assert_eq!(resources.len(), 2);
1066 assert_eq!(resources.get("foo").unwrap().as_ref(), b"foovalue");
1067 assert_eq!(resources.get("another").unwrap().as_ref(), b"value2");
1068 }
1069
1070 #[test]
1071 fn test_in_memory_shared_library() {
1072 let resource = Resource {
1073 name: Cow::from("foo"),
1074 in_memory_shared_library: Some(Cow::from(b"library".to_vec())),
1075 ..Resource::default()
1076 };
1077
1078 let mut data = Vec::new();
1079 write_packed_resources_v3(&[resource], &mut data, None).unwrap();
1080 let resources = load_resources(&data)
1081 .unwrap()
1082 .collect::<Result<Vec<Resource<u8>>, &'static str>>()
1083 .unwrap();
1084
1085 assert_eq!(resources.len(), 1);
1086
1087 let entry = &resources[0];
1088
1089 assert_eq!(
1090 entry.in_memory_shared_library.as_ref().unwrap().as_ref(),
1091 b"library"
1092 );
1093
1094 assert_eq!(
1095 entry,
1096 &Resource {
1097 name: Cow::from("foo"),
1098 in_memory_shared_library: Some(Cow::Borrowed(&data[data.len() - 7..data.len()])),
1099 ..Resource::default()
1100 }
1101 );
1102 }
1103
1104 #[test]
1105 fn test_shared_library_dependency_names() {
1106 let names = vec![Cow::from("depends"), Cow::from("libfoo")];
1107
1108 let resource = Resource {
1109 name: Cow::from("foo"),
1110 shared_library_dependency_names: Some(names),
1111 ..Resource::default()
1112 };
1113
1114 let mut data = Vec::new();
1115 write_packed_resources_v3(&[resource], &mut data, None).unwrap();
1116 let resources = load_resources(&data)
1117 .unwrap()
1118 .collect::<Result<Vec<Resource<u8>>, &'static str>>()
1119 .unwrap();
1120
1121 assert_eq!(resources.len(), 1);
1122
1123 let entry = &resources[0];
1124
1125 assert_eq!(
1126 entry.shared_library_dependency_names,
1127 Some(vec![Cow::Borrowed("depends"), Cow::Borrowed("libfoo")])
1128 );
1129 }
1130
1131 #[test]
1132 fn test_relative_path_module_source() {
1133 let resource = Resource {
1134 name: Cow::from("foo"),
1135 relative_path_module_source: Some(Cow::from(Path::new("foo.py"))),
1136 ..Resource::default()
1137 };
1138
1139 let mut data = Vec::new();
1140 write_packed_resources_v3(&[resource], &mut data, None).unwrap();
1141 let resources = load_resources(&data)
1142 .unwrap()
1143 .collect::<Result<Vec<Resource<u8>>, &'static str>>()
1144 .unwrap();
1145
1146 assert_eq!(resources.len(), 1);
1147
1148 let entry = &resources[0];
1149
1150 assert_eq!(
1151 entry,
1152 &Resource {
1153 name: Cow::Borrowed("foo"),
1154 relative_path_module_source: Some(Cow::Borrowed(Path::new("foo.py"))),
1155 ..Resource::default()
1156 }
1157 );
1158 }
1159
1160 #[test]
1161 fn test_relative_path_module_bytecode() {
1162 let resource = Resource {
1163 name: Cow::from("foo"),
1164 relative_path_module_bytecode: Some(Cow::from(Path::new("foo.pyc"))),
1165 ..Resource::default()
1166 };
1167
1168 let mut data = Vec::new();
1169 write_packed_resources_v3(&[resource], &mut data, None).unwrap();
1170 let resources = load_resources(&data)
1171 .unwrap()
1172 .collect::<Result<Vec<Resource<u8>>, &'static str>>()
1173 .unwrap();
1174
1175 assert_eq!(resources.len(), 1);
1176
1177 let entry = &resources[0];
1178
1179 assert_eq!(
1180 entry,
1181 &Resource {
1182 name: Cow::Borrowed("foo"),
1183 relative_path_module_bytecode: Some(Cow::Borrowed(Path::new("foo.pyc"))),
1184 ..Resource::default()
1185 }
1186 );
1187 }
1188
1189 #[test]
1190 fn test_relative_path_module_bytecode_opt1() {
1191 let resource = Resource {
1192 name: Cow::from("foo"),
1193 relative_path_module_bytecode_opt1: Some(Cow::from(Path::new("foo.O1.pyc"))),
1194 ..Resource::default()
1195 };
1196
1197 let mut data = Vec::new();
1198 write_packed_resources_v3(&[resource], &mut data, None).unwrap();
1199 let resources = load_resources(&data)
1200 .unwrap()
1201 .collect::<Result<Vec<Resource<u8>>, &'static str>>()
1202 .unwrap();
1203
1204 assert_eq!(resources.len(), 1);
1205
1206 let entry = &resources[0];
1207
1208 assert_eq!(
1209 entry,
1210 &Resource {
1211 name: Cow::Borrowed("foo"),
1212 relative_path_module_bytecode_opt1: Some(Cow::Borrowed(Path::new("foo.O1.pyc"))),
1213 ..Resource::default()
1214 }
1215 );
1216 }
1217
1218 #[test]
1219 fn test_relative_path_module_bytecode_opt2() {
1220 let resource = Resource {
1221 name: Cow::from("foo"),
1222 relative_path_module_bytecode_opt2: Some(Cow::from(Path::new("foo.O2.pyc"))),
1223 ..Resource::default()
1224 };
1225
1226 let mut data = Vec::new();
1227 write_packed_resources_v3(&[resource], &mut data, None).unwrap();
1228 let resources = load_resources(&data)
1229 .unwrap()
1230 .collect::<Result<Vec<Resource<u8>>, &'static str>>()
1231 .unwrap();
1232
1233 assert_eq!(resources.len(), 1);
1234
1235 let entry = &resources[0];
1236
1237 assert_eq!(
1238 entry,
1239 &Resource {
1240 name: Cow::Borrowed("foo"),
1241 relative_path_module_bytecode_opt2: Some(Cow::Borrowed(Path::new("foo.O2.pyc"))),
1242 ..Resource::default()
1243 }
1244 );
1245 }
1246
1247 #[test]
1248 fn test_relative_path_extension_module_shared_library() {
1249 let resource = Resource {
1250 name: Cow::from("foo"),
1251 relative_path_extension_module_shared_library: Some(Cow::from(Path::new("foo.so"))),
1252 ..Resource::default()
1253 };
1254
1255 let mut data = Vec::new();
1256 write_packed_resources_v3(&[resource], &mut data, None).unwrap();
1257 let resources = load_resources(&data)
1258 .unwrap()
1259 .collect::<Result<Vec<Resource<u8>>, &'static str>>()
1260 .unwrap();
1261
1262 assert_eq!(resources.len(), 1);
1263
1264 let entry = &resources[0];
1265
1266 assert_eq!(
1267 entry,
1268 &Resource {
1269 name: Cow::Borrowed("foo"),
1270 relative_path_extension_module_shared_library: Some(Cow::Borrowed(Path::new(
1271 "foo.so"
1272 ))),
1273 ..Resource::default()
1274 }
1275 );
1276 }
1277
1278 #[test]
1279 fn test_relative_path_package_resources() {
1280 let mut resources = HashMap::new();
1281 resources.insert(Cow::from("foo"), Cow::from(Path::new("foo")));
1282 resources.insert(Cow::from("another"), Cow::from(Path::new("another")));
1283
1284 let resource = Resource {
1285 name: Cow::from("foo"),
1286 relative_path_package_resources: Some(resources),
1287 ..Resource::default()
1288 };
1289
1290 let mut data = Vec::new();
1291 write_packed_resources_v3(&[resource], &mut data, None).unwrap();
1292 let resources = load_resources(&data)
1293 .unwrap()
1294 .collect::<Result<Vec<Resource<u8>>, &'static str>>()
1295 .unwrap();
1296
1297 assert_eq!(resources.len(), 1);
1298
1299 let entry = &resources[0];
1300
1301 let resources = entry.relative_path_package_resources.as_ref().unwrap();
1302 assert_eq!(resources.len(), 2);
1303 assert_eq!(resources.get("foo"), Some(&Cow::Borrowed(Path::new("foo"))));
1304 assert_eq!(
1305 resources.get("another"),
1306 Some(&Cow::Borrowed(Path::new("another")))
1307 );
1308 }
1309
1310 #[test]
1311 fn test_relative_path_package_distribution() {
1312 let mut resources = HashMap::new();
1313 resources.insert(Cow::from("foo"), Cow::from(Path::new("package/foo")));
1314 resources.insert(
1315 Cow::from("another"),
1316 Cow::from(Path::new("package/another")),
1317 );
1318
1319 let resource = Resource {
1320 name: Cow::from("foo"),
1321 relative_path_distribution_resources: Some(resources),
1322 ..Resource::default()
1323 };
1324
1325 let mut data = Vec::new();
1326 write_packed_resources_v3(&[resource], &mut data, None).unwrap();
1327 let resources = load_resources(&data)
1328 .unwrap()
1329 .collect::<Result<Vec<Resource<u8>>, &'static str>>()
1330 .unwrap();
1331
1332 assert_eq!(resources.len(), 1);
1333
1334 let entry = &resources[0];
1335
1336 let resources = entry.relative_path_distribution_resources.as_ref().unwrap();
1337 assert_eq!(resources.len(), 2);
1338 assert_eq!(
1339 resources.get("foo"),
1340 Some(&Cow::Borrowed(Path::new("package/foo")))
1341 );
1342 assert_eq!(
1343 resources.get("another"),
1344 Some(&Cow::Borrowed(Path::new("package/another")))
1345 );
1346 }
1347
1348 #[allow(clippy::cognitive_complexity)]
1349 #[test]
1350 fn test_all_fields() {
1351 let mut in_memory_resources = HashMap::new();
1352 in_memory_resources.insert(
1353 Cow::from("foo".to_string()),
1354 Cow::from(b"foovalue".to_vec()),
1355 );
1356 in_memory_resources.insert(Cow::from("resource2"), Cow::from(b"value2".to_vec()));
1357
1358 let mut in_memory_distribution = HashMap::new();
1359 in_memory_distribution.insert(Cow::from("dist"), Cow::from(b"distvalue".to_vec()));
1360 in_memory_distribution.insert(Cow::from("dist2"), Cow::from(b"dist2value".to_vec()));
1361
1362 let mut relative_path_resources = HashMap::new();
1363 relative_path_resources.insert(
1364 Cow::from("resource.txt"),
1365 Cow::from(Path::new("resource.txt")),
1366 );
1367 relative_path_resources.insert(Cow::from("foo.txt"), Cow::from(Path::new("foo.txt")));
1368
1369 let mut relative_path_distribution = HashMap::new();
1370 relative_path_distribution.insert(
1371 Cow::from("foo.txt"),
1372 Cow::from(Path::new("package/foo.txt")),
1373 );
1374 relative_path_distribution.insert(
1375 Cow::from("resource.txt"),
1376 Cow::from(Path::new("package/resource.txt")),
1377 );
1378
1379 let resource = Resource {
1380 name: Cow::from("module"),
1381 is_python_package: true,
1382 is_python_namespace_package: true,
1383 in_memory_source: Some(Cow::from(b"source".to_vec())),
1384 in_memory_bytecode: Some(Cow::from(b"bytecode".to_vec())),
1385 in_memory_bytecode_opt1: Some(Cow::from(b"bytecodeopt1".to_vec())),
1386 in_memory_bytecode_opt2: Some(Cow::from(b"bytecodeopt2".to_vec())),
1387 in_memory_extension_module_shared_library: Some(Cow::from(b"library".to_vec())),
1388 in_memory_package_resources: Some(in_memory_resources),
1389 in_memory_distribution_resources: Some(in_memory_distribution),
1390 in_memory_shared_library: Some(Cow::from(b"library".to_vec())),
1391 shared_library_dependency_names: Some(vec![Cow::from("libfoo"), Cow::from("depends")]),
1392 relative_path_module_source: Some(Cow::from(Path::new("source_path"))),
1393 relative_path_module_bytecode: Some(Cow::from(Path::new("bytecode_path"))),
1394 relative_path_module_bytecode_opt1: Some(Cow::from(Path::new("bytecode_opt1_path"))),
1395 relative_path_module_bytecode_opt2: Some(Cow::from(Path::new("bytecode_opt2_path"))),
1396 relative_path_extension_module_shared_library: Some(Cow::from(Path::new("em_path"))),
1397 relative_path_package_resources: Some(relative_path_resources),
1398 relative_path_distribution_resources: Some(relative_path_distribution),
1399 is_python_module: true,
1400 is_python_builtin_extension_module: true,
1401 is_python_frozen_module: true,
1402 is_python_extension_module: true,
1403 is_shared_library: true,
1404 is_utf8_filename_data: true,
1405 file_executable: true,
1406 file_data_embedded: Some(Cow::from(b"file_data_embedded".to_vec())),
1407 file_data_utf8_relative_path: Some(Cow::from("file_data_utf8_relative_path")),
1408 };
1409
1410 let mut data = Vec::new();
1411 write_packed_resources_v3(&[resource], &mut data, None).unwrap();
1412 let resources = load_resources(&data)
1413 .unwrap()
1414 .collect::<Result<Vec<Resource<u8>>, &'static str>>()
1415 .unwrap();
1416
1417 assert_eq!(resources.len(), 1);
1418
1419 let entry = &resources[0];
1420
1421 assert!(entry.is_python_package);
1422 assert!(entry.is_python_namespace_package);
1423 assert_eq!(entry.in_memory_source.as_ref().unwrap().as_ref(), b"source");
1424 assert_eq!(
1425 entry.in_memory_bytecode.as_ref().unwrap().as_ref(),
1426 b"bytecode"
1427 );
1428 assert_eq!(
1429 entry.in_memory_bytecode_opt1.as_ref().unwrap().as_ref(),
1430 b"bytecodeopt1"
1431 );
1432 assert_eq!(
1433 entry.in_memory_bytecode_opt2.as_ref().unwrap().as_ref(),
1434 b"bytecodeopt2"
1435 );
1436 assert_eq!(
1437 entry
1438 .in_memory_extension_module_shared_library
1439 .as_ref()
1440 .unwrap()
1441 .as_ref(),
1442 b"library"
1443 );
1444
1445 let resources = entry.in_memory_package_resources.as_ref().unwrap();
1446 assert_eq!(resources.len(), 2);
1447 assert_eq!(resources.get("foo").unwrap().as_ref(), b"foovalue");
1448 assert_eq!(resources.get("resource2").unwrap().as_ref(), b"value2");
1449
1450 let resources = entry.in_memory_distribution_resources.as_ref().unwrap();
1451 assert_eq!(resources.len(), 2);
1452 assert_eq!(resources.get("dist").unwrap().as_ref(), b"distvalue");
1453 assert_eq!(resources.get("dist2").unwrap().as_ref(), b"dist2value");
1454
1455 assert_eq!(
1456 entry.in_memory_shared_library.as_ref().unwrap().as_ref(),
1457 b"library"
1458 );
1459 assert_eq!(
1460 entry.shared_library_dependency_names.as_ref().unwrap(),
1461 &vec!["libfoo", "depends"]
1462 );
1463
1464 assert_eq!(
1465 entry.relative_path_module_source,
1466 Some(Cow::Borrowed(Path::new("source_path")))
1467 );
1468
1469 assert_eq!(
1470 entry.relative_path_module_bytecode,
1471 Some(Cow::Borrowed(Path::new("bytecode_path")))
1472 );
1473 assert_eq!(
1474 entry.relative_path_module_bytecode_opt1,
1475 Some(Cow::Borrowed(Path::new("bytecode_opt1_path")))
1476 );
1477 assert_eq!(
1478 entry.relative_path_module_bytecode_opt2,
1479 Some(Cow::Borrowed(Path::new("bytecode_opt2_path")))
1480 );
1481 assert_eq!(
1482 entry.relative_path_extension_module_shared_library,
1483 Some(Cow::Borrowed(Path::new("em_path")))
1484 );
1485
1486 let resources = entry.relative_path_package_resources.as_ref().unwrap();
1487 assert_eq!(resources.len(), 2);
1488 assert_eq!(
1489 resources.get("resource.txt"),
1490 Some(&Cow::Borrowed(Path::new("resource.txt")))
1491 );
1492 assert_eq!(
1493 resources.get("foo.txt"),
1494 Some(&Cow::Borrowed(Path::new("foo.txt")))
1495 );
1496
1497 let distribution = entry.relative_path_distribution_resources.as_ref().unwrap();
1498 assert_eq!(distribution.len(), 2);
1499 assert_eq!(
1500 distribution.get("foo.txt"),
1501 Some(&Cow::Borrowed(Path::new("package/foo.txt")))
1502 );
1503 assert_eq!(
1504 distribution.get("resource.txt"),
1505 Some(&Cow::Borrowed(Path::new("package/resource.txt")))
1506 );
1507 assert!(entry.is_python_module);
1508 assert!(entry.is_python_builtin_extension_module);
1509 assert!(entry.is_python_frozen_module);
1510 assert!(entry.is_python_extension_module);
1511 assert!(entry.is_shared_library);
1512 assert!(entry.is_utf8_filename_data);
1513 assert!(entry.file_executable);
1514 assert_eq!(
1515 entry.file_data_embedded.as_ref().unwrap().as_ref(),
1516 b"file_data_embedded"
1517 );
1518 assert_eq!(
1519 entry.file_data_utf8_relative_path.as_ref().unwrap(),
1520 "file_data_utf8_relative_path"
1521 );
1522 }
1523
1524 #[test]
1525 fn test_fields_mix() {
1526 let resources: Vec<Resource<u8>> = vec![
1527 Resource {
1528 name: Cow::from("foo"),
1529 is_python_module: true,
1530 in_memory_source: Some(Cow::from(b"import io".to_vec())),
1531 ..Resource::default()
1532 },
1533 Resource {
1534 name: Cow::from("bar"),
1535 is_python_module: true,
1536 in_memory_bytecode: Some(Cow::from(b"fake bytecode".to_vec())),
1537 ..Resource::default()
1538 },
1539 ];
1540
1541 let mut data = Vec::new();
1542 write_packed_resources_v3(&resources, &mut data, None).unwrap();
1543 let loaded = load_resources(&data)
1544 .unwrap()
1545 .collect::<Result<Vec<Resource<u8>>, &'static str>>()
1546 .unwrap();
1547
1548 assert_eq!(resources, loaded);
1549 }
1550}