python_packed_resources/
parser.rs

1// Copyright 2022 Gregory Szorc.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9/*! Parsing of packed resources data blobs. */
10
11use {
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/// Represents a blob section in the blob index.
26#[derive(Debug)]
27struct BlobSection {
28    resource_field: u8,
29    raw_payload_length: usize,
30    interior_padding: Option<BlobInteriorPadding>,
31}
32
33/// Holds state used to read an individual blob section.
34#[derive(Clone, Copy, Debug)]
35struct BlobSectionReadState {
36    offset: usize,
37    interior_padding: BlobInteriorPadding,
38}
39
40/// An iterator over an actively parsed packed resources data structure.
41///
42/// The iterator emits [Resource] instances. The index data for a given resource is
43/// not read or validated until the iterator attempts to deserialize it.
44pub 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    /// The expected number of resources we will emit.
55    pub fn expected_resources_count(&self) -> usize {
56        self.claimed_resources_count
57    }
58
59    /// Resolve a slice to an individual blob's data.
60    ///
61    /// This accepts a reference to the original blobs payload, an array of
62    /// current blob section offsets, the resource field being accessed, and the
63    /// length of the blob and returns a slice to that blob.
64    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        // There isn't an API that lets us get a OsStr from &[u16]. So we need to use
93        // owned types.
94        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
508/// Parse a packed resources data structure.
509///
510/// The data structure is parsed lazily via an iterator that emits reconstructed
511/// [Resource] instances.
512///
513/// Performance note: we once attempted to switch to anyhow for error handling and
514/// this decreased performance by ~15%. Given the performance sensitivity of this
515/// code, we need to keep error handling primitive.
516pub 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    // Array indexing resource field to current payload offset within that section.
619    let mut blob_offsets: [Option<BlobSectionReadState>; 256] = [None; 256];
620
621    // Global payload offset where blobs data starts.
622    let blob_start_offset: usize =
623            // Global header.
624            1 + 4 + 4 + 4
625            + blob_index_length
626            + resources_index_length
627        ;
628    // Current offset from start of blobs data.
629    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    // Same as above just with null interior padding.
794    #[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}