Skip to main content

ooxmlsdk/common/
package.rs

1use std::borrow::Cow;
2use std::collections::{HashMap, HashSet};
3use std::io::{Cursor, Read, Seek, SeekFrom};
4use std::path::{Path, PathBuf};
5use std::sync::atomic::{AtomicU64, Ordering};
6use std::sync::{Arc, OnceLock};
7
8use bytes::Bytes;
9
10#[cfg(feature = "flat-opc")]
11use super::unexpected_eof;
12use super::{
13  SdkError, part_relationships_path, resolve_relationship_target_path, resolve_zip_file_path,
14};
15use crate::schemas::opc_content_types::{Types, TypesChoice};
16use crate::schemas::opc_relationships::{
17  Relationship as OpcRelationship, Relationships, TargetMode,
18};
19
20#[cfg(feature = "flat-opc")]
21const FLAT_OPC_PACKAGE_NS: &str = "http://schemas.microsoft.com/office/2006/xmlPackage";
22const RELATIONSHIP_CONTENT_TYPE: &str = "application/vnd.openxmlformats-package.relationships+xml";
23
24#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
25pub struct PartId(u32);
26
27impl PartId {
28  #[inline]
29  pub const fn from_index(index: usize) -> Self {
30    Self(index as u32)
31  }
32
33  #[inline]
34  pub const fn index(self) -> usize {
35    self.0 as usize
36  }
37}
38
39#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
40pub(crate) struct PackageId(u64);
41
42impl PackageId {
43  fn new() -> Self {
44    static NEXT_PACKAGE_ID: AtomicU64 = AtomicU64::new(1);
45    Self(NEXT_PACKAGE_ID.fetch_add(1, Ordering::Relaxed))
46  }
47}
48
49#[derive(Clone, Debug, Eq, PartialEq)]
50pub(crate) struct NewPartDescriptor {
51  pub(crate) relationship_type: Cow<'static, str>,
52  pub(crate) content_type: Cow<'static, str>,
53  pub(crate) path_prefix: &'static str,
54  pub(crate) target_name: &'static str,
55  pub(crate) extension: Cow<'static, str>,
56}
57
58pub(crate) fn default_part_extension_for_content_type(content_type: &str) -> Option<&'static str> {
59  match content_type {
60    "image/unknown" => Some(".bin"),
61    "image/bmp" => Some(".bmp"),
62    "image/gif" => Some(".gif"),
63    "image/png" => Some(".png"),
64    "image/jp2" => Some(".jp2"),
65    "image/tif" => Some(".tif"),
66    "image/tiff" => Some(".tiff"),
67    "image/xbm" => Some(".xbm"),
68    "image/x-icon" => Some(".ico"),
69    "image/x-pcx" => Some(".pcx"),
70    "image/x-pcz" => Some(".pcz"),
71    "image/x-emz" => Some(".emz"),
72    "image/x-wmz" => Some(".wmz"),
73    "image/jpeg" => Some(".jpeg"),
74    "image/x-emf" => Some(".emf"),
75    "image/x-wmf" => Some(".wmf"),
76    "image/svg+xml" => Some(".svg"),
77    "audio/aiff" => Some(".aiff"),
78    "audio/midi" => Some(".midi"),
79    "audio/mp3" => Some(".mp3"),
80    "audio/mpegurl" => Some(".m3u"),
81    "audio/wav" => Some(".wav"),
82    "audio/x-ms-wma" => Some(".wma"),
83    "audio/mpeg" => Some(".mpeg"),
84    "audio/ogg" => Some(".ogg"),
85    "video/x-ms-asf-plugin" => Some(".asx"),
86    "video/avi" => Some(".avi"),
87    "video/mp4" => Some(".mp4"),
88    "video/mpg" => Some(".mpg"),
89    "video/mpeg" => Some(".mpeg"),
90    "video/x-ms-wmv" => Some(".wmv"),
91    "video/x-ms-wmx" => Some(".wmx"),
92    "video/x-ms-wvx" => Some(".wvx"),
93    "video/quicktime" => Some(".mov"),
94    "video/ogg" => Some(".ogg"),
95    "video/vc1" => Some(".wmv"),
96    _ => None,
97  }
98}
99
100#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
101pub enum NewPartTargetMode {
102  Fixed,
103  #[default]
104  Indexed,
105}
106
107#[derive(Clone, Debug)]
108pub(crate) enum StoredPartData {
109  Archived {
110    entry_index: usize,
111    bytes: OnceLock<Bytes>,
112  },
113  Owned {
114    bytes: Bytes,
115    original_entry_index: Option<usize>,
116  },
117}
118
119impl StoredPartData {
120  #[inline]
121  fn cached_bytes(&self) -> Option<&[u8]> {
122    match self {
123      Self::Archived { bytes, .. } => bytes.get().map(AsRef::as_ref),
124      Self::Owned { bytes, .. } => Some(bytes.as_ref()),
125    }
126  }
127
128  #[inline]
129  fn original_entry_index(&self) -> Option<usize> {
130    match self {
131      Self::Archived { entry_index, .. } => Some(*entry_index),
132      Self::Owned {
133        original_entry_index,
134        ..
135      } => *original_entry_index,
136    }
137  }
138
139  #[inline]
140  fn unchanged_archive_entry_index(&self) -> Option<usize> {
141    match self {
142      Self::Archived { entry_index, .. } => Some(*entry_index),
143      Self::Owned { .. } => None,
144    }
145  }
146
147  #[inline]
148  fn set_owned(&mut self, bytes: Vec<u8>) {
149    let original_entry_index = self.original_entry_index();
150    *self = Self::Owned {
151      bytes: bytes.into(),
152      original_entry_index,
153    };
154  }
155}
156
157#[derive(Clone, Copy, Debug, Eq, PartialEq)]
158pub(crate) enum PackageSaveEntry {
159  ArchivedExtra(usize),
160  ContentTypes,
161  PackageRelationships,
162  Part(PartId),
163  PartRelationships(PartId),
164}
165
166#[derive(Clone, Debug)]
167enum ArchiveReader {
168  #[cfg(any(unix, windows))]
169  File(PositionedFileReader),
170  Memory(Cursor<Bytes>),
171}
172
173impl Read for ArchiveReader {
174  fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
175    match self {
176      #[cfg(any(unix, windows))]
177      Self::File(reader) => reader.read(buffer),
178      Self::Memory(reader) => reader.read(buffer),
179    }
180  }
181}
182
183impl Seek for ArchiveReader {
184  fn seek(&mut self, position: SeekFrom) -> std::io::Result<u64> {
185    match self {
186      #[cfg(any(unix, windows))]
187      Self::File(reader) => reader.seek(position),
188      Self::Memory(reader) => reader.seek(position),
189    }
190  }
191}
192
193#[cfg(any(unix, windows))]
194#[derive(Clone, Debug)]
195struct PositionedFileReader {
196  file: Arc<std::fs::File>,
197  position: u64,
198  length: u64,
199}
200
201#[cfg(any(unix, windows))]
202impl PositionedFileReader {
203  fn new(file: std::fs::File) -> Result<Self, SdkError> {
204    let length = file.metadata()?.len();
205    Ok(Self {
206      file: Arc::new(file),
207      position: 0,
208      length,
209    })
210  }
211}
212
213#[cfg(any(unix, windows))]
214impl Read for PositionedFileReader {
215  fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
216    #[cfg(unix)]
217    let read = {
218      use std::os::unix::fs::FileExt as _;
219      self.file.read_at(buffer, self.position)?
220    };
221    #[cfg(windows)]
222    let read = {
223      use std::os::windows::fs::FileExt as _;
224      self.file.seek_read(buffer, self.position)?
225    };
226    self.position = self
227      .position
228      .checked_add(read as u64)
229      .ok_or_else(|| std::io::Error::other("file reader position overflow"))?;
230    Ok(read)
231  }
232}
233
234#[cfg(any(unix, windows))]
235impl Seek for PositionedFileReader {
236  fn seek(&mut self, position: SeekFrom) -> std::io::Result<u64> {
237    let next = match position {
238      SeekFrom::Start(position) => i128::from(position),
239      SeekFrom::Current(offset) => i128::from(self.position) + i128::from(offset),
240      SeekFrom::End(offset) => i128::from(self.length) + i128::from(offset),
241    };
242    self.position = u64::try_from(next)
243      .map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidInput, "invalid seek"))?;
244    Ok(self.position)
245  }
246}
247
248#[derive(Debug)]
249struct ArchiveBacking {
250  archive: zip::ZipArchive<ArchiveReader>,
251}
252
253impl ArchiveBacking {
254  fn read_entry(&self, entry_index: usize) -> Result<Bytes, SdkError> {
255    let mut archive = self.archive.clone();
256    let bytes = read_archive_entry_by_index(&mut archive, entry_index)?;
257    Ok(bytes.into())
258  }
259
260  fn raw_copy_entry<W: std::io::Write + std::io::Seek>(
261    &self,
262    entry_index: usize,
263    target_name: &str,
264    writer: &mut zip::ZipWriter<W>,
265  ) -> Result<(), SdkError> {
266    let mut archive = self.archive.clone();
267    let entry = archive.by_index_raw(entry_index)?;
268    writer.raw_copy_file_rename(entry, target_name)?;
269    Ok(())
270  }
271
272  fn raw_copy_entry_with_original_name<W: std::io::Write + std::io::Seek>(
273    &self,
274    entry_index: usize,
275    writer: &mut zip::ZipWriter<W>,
276  ) -> Result<(), SdkError> {
277    let mut archive = self.archive.clone();
278    let entry = archive.by_index_raw(entry_index)?;
279    writer.raw_copy_file(entry)?;
280    Ok(())
281  }
282
283  fn configure_writer<W: std::io::Write + std::io::Seek>(
284    &self,
285    writer: &mut zip::ZipWriter<W>,
286  ) -> Result<(), SdkError> {
287    writer.set_raw_comment(self.archive.comment().into())?;
288    if let Some(data) = self.archive.raw_zip64_extensible_data_sector() {
289      writer.set_raw_zip64_extensible_data_sector(data.into());
290    }
291    Ok(())
292  }
293}
294
295fn open_package_file(path: &Path) -> Result<std::fs::File, SdkError> {
296  let mut options = std::fs::OpenOptions::new();
297  options.read(true);
298  #[cfg(windows)]
299  {
300    use std::os::windows::fs::OpenOptionsExt as _;
301    const FILE_SHARE_READ: u32 = 0x0000_0001;
302    const FILE_SHARE_WRITE: u32 = 0x0000_0002;
303    const FILE_SHARE_DELETE: u32 = 0x0000_0004;
304    options.share_mode(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE);
305  }
306  Ok(options.open(path)?)
307}
308
309pub(crate) fn create_package_temp_file(
310  target_path: &Path,
311) -> Result<(PathBuf, std::fs::File), SdkError> {
312  static NEXT_TEMP_FILE_ID: AtomicU64 = AtomicU64::new(1);
313  let parent = target_path.parent().unwrap_or_else(|| Path::new("."));
314  let file_name = target_path
315    .file_name()
316    .and_then(|name| name.to_str())
317    .unwrap_or("package");
318
319  for _ in 0..1024 {
320    let id = NEXT_TEMP_FILE_ID.fetch_add(1, Ordering::Relaxed);
321    let path = parent.join(format!(
322      ".{file_name}.ooxmlsdk-{}-{id}.tmp",
323      std::process::id()
324    ));
325    match std::fs::OpenOptions::new()
326      .write(true)
327      .create_new(true)
328      .open(&path)
329    {
330      Ok(file) => return Ok((path, file)),
331      Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => {}
332      Err(error) => return Err(error.into()),
333    }
334  }
335
336  Err(SdkError::CommonError(format!(
337    "could not create a temporary package file beside {}",
338    target_path.display()
339  )))
340}
341
342pub(crate) fn replace_package_file(
343  temporary_path: &Path,
344  target_path: &Path,
345) -> Result<(), SdkError> {
346  #[cfg(unix)]
347  {
348    std::fs::rename(temporary_path, target_path)?;
349    Ok(())
350  }
351
352  #[cfg(not(unix))]
353  {
354    if !target_path.exists() {
355      std::fs::rename(temporary_path, target_path)?;
356      return Ok(());
357    }
358
359    let backup_path = target_path.with_extension(format!(
360      "ooxmlsdk-backup-{}-{}",
361      std::process::id(),
362      PackageId::new().0
363    ));
364    std::fs::rename(target_path, &backup_path)?;
365    if let Err(error) = std::fs::rename(temporary_path, target_path) {
366      let _ = std::fs::rename(&backup_path, target_path);
367      return Err(error.into());
368    }
369    let _ = std::fs::remove_file(backup_path);
370    Ok(())
371  }
372}
373
374#[derive(Clone, Debug, Eq, PartialEq)]
375pub(crate) struct RelationshipInfo {
376  id: Box<str>,
377  relationship_type: super::XmlRelationshipNamespaceUri,
378  target: Box<str>,
379  target_mode: Option<TargetMode>,
380  target_kind: RelationshipTargetKind,
381  target_part_id: Option<PartId>,
382}
383
384#[derive(Clone, Copy, Debug, Eq, PartialEq)]
385pub enum RelationshipTargetKind {
386  InternalPart,
387  External,
388  Null,
389  Missing,
390}
391
392#[derive(Clone, Copy, Debug, Eq, PartialEq)]
393pub enum ReferenceRelationshipKind {
394  External,
395  Hyperlink,
396  Audio,
397  Media,
398  Video,
399}
400
401impl RelationshipInfo {
402  fn internal_part(
403    id: String,
404    relationship_type: String,
405    target: String,
406    target_part_id: PartId,
407  ) -> Self {
408    Self {
409      id: id.into_boxed_str(),
410      relationship_type: super::XmlRelationshipNamespaceUri::from_uri(&relationship_type),
411      target: target.into_boxed_str(),
412      target_mode: None,
413      target_kind: RelationshipTargetKind::InternalPart,
414      target_part_id: Some(target_part_id),
415    }
416  }
417
418  fn external(
419    id: String,
420    relationship_type: String,
421    target: String,
422    target_mode: Option<TargetMode>,
423  ) -> Self {
424    Self {
425      id: id.into_boxed_str(),
426      relationship_type: super::XmlRelationshipNamespaceUri::from_uri(&relationship_type),
427      target: target.into_boxed_str(),
428      target_mode,
429      target_kind: RelationshipTargetKind::External,
430      target_part_id: None,
431    }
432  }
433
434  fn reference(
435    id: String,
436    relationship_type: String,
437    target: String,
438    target_mode: Option<TargetMode>,
439  ) -> Self {
440    let effective_target_mode = target_mode.unwrap_or(TargetMode::Internal);
441    let target_kind = if matches!(effective_target_mode, TargetMode::External) {
442      RelationshipTargetKind::External
443    } else if target.eq_ignore_ascii_case("NULL") {
444      RelationshipTargetKind::Null
445    } else {
446      RelationshipTargetKind::Missing
447    };
448    Self {
449      id: id.into_boxed_str(),
450      relationship_type: super::XmlRelationshipNamespaceUri::from_uri(&relationship_type),
451      target: target.into_boxed_str(),
452      target_mode,
453      target_kind,
454      target_part_id: None,
455    }
456  }
457
458  #[inline]
459  pub fn id(&self) -> &str {
460    &self.id
461  }
462
463  #[inline]
464  pub fn relationship_type(&self) -> &str {
465    self.relationship_type.as_str()
466  }
467
468  #[inline]
469  pub(crate) fn relationship_type_bytes(&self) -> &[u8] {
470    self.relationship_type.uri_bytes()
471  }
472
473  #[inline]
474  pub fn target(&self) -> &str {
475    &self.target
476  }
477
478  #[inline]
479  pub fn target_mode(&self) -> TargetMode {
480    self.target_mode.unwrap_or(TargetMode::Internal)
481  }
482
483  #[inline]
484  pub fn target_kind(&self) -> RelationshipTargetKind {
485    self.target_kind
486  }
487
488  #[inline]
489  pub fn target_part_id(&self) -> Option<PartId> {
490    self.target_part_id
491  }
492
493  #[inline]
494  pub fn is_reference_relationship(&self) -> bool {
495    self.reference_kind().is_some()
496  }
497
498  #[inline]
499  pub fn reference_kind(&self) -> Option<ReferenceRelationshipKind> {
500    let relationship_type = self.relationship_type_bytes();
501    if super::relationship_type_matches_bytes(relationship_type, super::REL_HYPERLINK) {
502      Some(ReferenceRelationshipKind::Hyperlink)
503    } else if super::relationship_type_matches_bytes(relationship_type, super::REL_AUDIO) {
504      Some(ReferenceRelationshipKind::Audio)
505    } else if super::relationship_type_matches_bytes(relationship_type, super::REL_MEDIA) {
506      Some(ReferenceRelationshipKind::Media)
507    } else if super::relationship_type_matches_bytes(relationship_type, super::REL_VIDEO) {
508      Some(ReferenceRelationshipKind::Video)
509    } else if self.target_kind == RelationshipTargetKind::External {
510      Some(ReferenceRelationshipKind::External)
511    } else {
512      None
513    }
514  }
515
516  fn to_relationship(&self) -> OpcRelationship {
517    OpcRelationship {
518      id: self.id().to_string(),
519      r#type: self.relationship_type().to_string(),
520      target: self.target().to_string(),
521      target_mode: self.target_mode,
522    }
523  }
524}
525
526#[derive(Clone, Debug, Eq, PartialEq)]
527pub struct Relationship {
528  inner: RelationshipInfo,
529}
530
531#[derive(Clone, Copy, Debug, Eq, PartialEq)]
532pub struct RelationshipRef<'a> {
533  inner: &'a RelationshipInfo,
534}
535
536macro_rules! impl_relationship_accessors {
537  ($ident:ident) => {
538    impl $ident {
539      #[inline]
540      pub fn id(&self) -> &str {
541        self.inner.id()
542      }
543
544      #[inline]
545      pub fn relationship_type(&self) -> &str {
546        self.inner.relationship_type()
547      }
548
549      #[inline]
550      pub fn target(&self) -> &str {
551        self.inner.target()
552      }
553
554      #[inline]
555      pub fn target_mode(&self) -> TargetMode {
556        self.inner.target_mode()
557      }
558
559      #[inline]
560      pub fn target_kind(&self) -> RelationshipTargetKind {
561        self.inner.target_kind()
562      }
563
564      #[inline]
565      pub fn target_part_id(&self) -> Option<PartId> {
566        self.inner.target_part_id()
567      }
568
569      #[inline]
570      pub fn reference_kind(&self) -> Option<ReferenceRelationshipKind> {
571        self.inner.reference_kind()
572      }
573
574      #[inline]
575      pub fn is_reference_relationship(&self) -> bool {
576        self.inner.is_reference_relationship()
577      }
578    }
579  };
580}
581
582impl_relationship_accessors!(Relationship);
583
584impl<'a> RelationshipRef<'a> {
585  pub const HYPERLINK_RELATIONSHIP_TYPE: &'static str =
586    "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
587  pub const AUDIO_REFERENCE_RELATIONSHIP_TYPE: &'static str =
588    "http://schemas.openxmlformats.org/officeDocument/2006/relationships/audio";
589  pub const MEDIA_REFERENCE_RELATIONSHIP_TYPE: &'static str =
590    "http://schemas.microsoft.com/office/2007/relationships/media";
591  pub const VIDEO_REFERENCE_RELATIONSHIP_TYPE: &'static str =
592    "http://schemas.openxmlformats.org/officeDocument/2006/relationships/video";
593
594  #[inline]
595  pub(crate) const fn new(inner: &'a RelationshipInfo) -> Self {
596    Self { inner }
597  }
598
599  #[inline]
600  pub fn id(&self) -> &'a str {
601    self.inner.id()
602  }
603
604  #[inline]
605  pub fn relationship_type(&self) -> &'a str {
606    self.inner.relationship_type()
607  }
608
609  #[inline]
610  pub fn target(&self) -> &'a str {
611    self.inner.target()
612  }
613
614  #[inline]
615  pub fn target_mode(&self) -> TargetMode {
616    self.inner.target_mode()
617  }
618
619  #[inline]
620  pub fn target_kind(&self) -> RelationshipTargetKind {
621    self.inner.target_kind()
622  }
623
624  #[inline]
625  pub fn target_part_id(&self) -> Option<PartId> {
626    self.inner.target_part_id()
627  }
628
629  #[inline]
630  pub fn reference_kind(&self) -> Option<ReferenceRelationshipKind> {
631    self.inner.reference_kind()
632  }
633
634  #[inline]
635  pub fn is_reference_relationship(&self) -> bool {
636    self.inner.is_reference_relationship()
637  }
638}
639
640impl From<RelationshipInfo> for Relationship {
641  #[inline]
642  fn from(inner: RelationshipInfo) -> Self {
643    Self { inner }
644  }
645}
646
647impl From<RelationshipRef<'_>> for Relationship {
648  #[inline]
649  fn from(value: RelationshipRef<'_>) -> Self {
650    Self {
651      inner: value.inner.clone(),
652    }
653  }
654}
655
656impl<'a> From<&'a RelationshipInfo> for RelationshipRef<'a> {
657  #[inline]
658  fn from(inner: &'a RelationshipInfo) -> Self {
659    Self::new(inner)
660  }
661}
662
663#[doc(hidden)]
664#[derive(Clone, Debug, Default, Eq, PartialEq)]
665pub struct RelationshipSet {
666  relationships: Vec<RelationshipInfo>,
667  by_id: HashMap<Box<str>, usize>,
668  raw_bytes: Option<Box<[u8]>>,
669  archive_entry_index: Option<usize>,
670}
671
672fn next_relationship_id<'a>(relationships: impl Iterator<Item = &'a RelationshipInfo>) -> String {
673  let next = relationships
674    .filter_map(|relationship| relationship.id().strip_prefix("rId"))
675    .filter_map(|suffix| suffix.parse::<u32>().ok())
676    .max()
677    .unwrap_or_default()
678    + 1;
679  format!("rId{next}")
680}
681
682impl RelationshipSet {
683  pub(crate) const HYPERLINK_RELATIONSHIP_TYPE: &'static str =
684    "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
685  pub(crate) const AUDIO_REFERENCE_RELATIONSHIP_TYPE: &'static str =
686    "http://schemas.openxmlformats.org/officeDocument/2006/relationships/audio";
687  pub(crate) const MEDIA_REFERENCE_RELATIONSHIP_TYPE: &'static str =
688    "http://schemas.microsoft.com/office/2007/relationships/media";
689  pub(crate) const VIDEO_REFERENCE_RELATIONSHIP_TYPE: &'static str =
690    "http://schemas.openxmlformats.org/officeDocument/2006/relationships/video";
691
692  #[inline]
693  pub(crate) fn is_empty(&self) -> bool {
694    self.relationships.is_empty() && self.raw_bytes.is_none()
695  }
696
697  #[inline]
698  pub(crate) fn iter(&self) -> impl Iterator<Item = &RelationshipInfo> {
699    self.relationships.iter()
700  }
701
702  #[inline]
703  pub(crate) fn get(&self, relationship_id: &str) -> Option<&RelationshipInfo> {
704    self
705      .by_id
706      .get(relationship_id)
707      .and_then(|index| self.relationships.get(*index))
708  }
709
710  #[inline]
711  pub(crate) fn contains_id(&self, relationship_id: &str) -> bool {
712    self.by_id.contains_key(relationship_id)
713  }
714
715  pub(crate) fn next_relationship_id(&self) -> String {
716    next_relationship_id(self.relationships.iter())
717  }
718
719  pub(crate) fn add_external_relationship(
720    &mut self,
721    relationship_id: impl Into<String>,
722    relationship_type: impl Into<String>,
723    target: impl Into<String>,
724  ) -> Result<&RelationshipInfo, SdkError> {
725    self.push_relationship(RelationshipInfo::external(
726      relationship_id.into(),
727      relationship_type.into(),
728      target.into(),
729      Some(TargetMode::External),
730    ))
731  }
732
733  pub(crate) fn add_hyperlink_relationship(
734    &mut self,
735    relationship_id: impl Into<String>,
736    target: impl Into<String>,
737  ) -> Result<&RelationshipInfo, SdkError> {
738    self.add_hyperlink_relationship_with_mode(relationship_id, target, TargetMode::External)
739  }
740
741  pub(crate) fn add_hyperlink_relationship_with_mode(
742    &mut self,
743    relationship_id: impl Into<String>,
744    target: impl Into<String>,
745    target_mode: TargetMode,
746  ) -> Result<&RelationshipInfo, SdkError> {
747    let target_mode = match target_mode {
748      TargetMode::External => Some(TargetMode::External),
749      TargetMode::Internal => None,
750    };
751    self.push_relationship(RelationshipInfo::reference(
752      relationship_id.into(),
753      Self::HYPERLINK_RELATIONSHIP_TYPE.to_string(),
754      target.into(),
755      target_mode,
756    ))
757  }
758
759  pub(crate) fn add_internal_part_relationship(
760    &mut self,
761    relationship_id: impl Into<String>,
762    relationship_type: impl Into<String>,
763    target: impl Into<String>,
764    target_part_id: PartId,
765  ) -> Result<&RelationshipInfo, SdkError> {
766    self.push_relationship(RelationshipInfo::internal_part(
767      relationship_id.into(),
768      relationship_type.into(),
769      target.into(),
770      target_part_id,
771    ))
772  }
773
774  pub(crate) fn add_relationship_info(
775    &mut self,
776    relationship: RelationshipInfo,
777  ) -> Result<&RelationshipInfo, SdkError> {
778    self.push_relationship(relationship)
779  }
780
781  pub(crate) fn remove(&mut self, relationship_id: &str) -> Option<RelationshipInfo> {
782    let index = *self.by_id.get(relationship_id)?;
783    self.raw_bytes = None;
784    let removed = self.relationships.remove(index);
785    self.rebuild_index();
786    Some(removed)
787  }
788
789  pub(crate) fn remove_reference_relationship(
790    &mut self,
791    relationship_id: &str,
792  ) -> Result<RelationshipInfo, SdkError> {
793    let relationship = self.get(relationship_id).ok_or_else(|| {
794      SdkError::CommonError(format!(
795        "reference relationship id {relationship_id} does not exist"
796      ))
797    })?;
798    if !relationship.is_reference_relationship() {
799      return Err(SdkError::CommonError(format!(
800        "relationship id {relationship_id} is not a reference relationship"
801      )));
802    }
803    Ok(
804      self
805        .remove(relationship_id)
806        .expect("relationship was already resolved"),
807    )
808  }
809
810  pub(crate) fn get_external_relationship(
811    &self,
812    relationship_id: &str,
813  ) -> Option<&RelationshipInfo> {
814    self.get(relationship_id).filter(|relationship| {
815      matches!(
816        relationship.reference_kind(),
817        Some(ReferenceRelationshipKind::External)
818      )
819    })
820  }
821
822  pub(crate) fn remove_external_relationship(
823    &mut self,
824    relationship_id: &str,
825  ) -> Result<RelationshipInfo, SdkError> {
826    let relationship = self.get(relationship_id).ok_or_else(|| {
827      SdkError::CommonError(format!(
828        "external relationship id {relationship_id} does not exist"
829      ))
830    })?;
831    if !matches!(
832      relationship.reference_kind(),
833      Some(ReferenceRelationshipKind::External)
834    ) {
835      return Err(SdkError::CommonError(format!(
836        "relationship id {relationship_id} is not an external relationship"
837      )));
838    }
839    Ok(
840      self
841        .remove(relationship_id)
842        .expect("relationship was already resolved"),
843    )
844  }
845
846  pub(crate) fn get_hyperlink_relationship(
847    &self,
848    relationship_id: &str,
849  ) -> Option<&RelationshipInfo> {
850    self.get(relationship_id).filter(|relationship| {
851      matches!(
852        relationship.reference_kind(),
853        Some(ReferenceRelationshipKind::Hyperlink)
854      )
855    })
856  }
857
858  pub(crate) fn change_relationship_id(
859    &mut self,
860    relationship_id: &str,
861    new_relationship_id: impl Into<String>,
862  ) -> Result<(), SdkError> {
863    let new_relationship_id = new_relationship_id.into();
864    if relationship_id == new_relationship_id {
865      return Ok(());
866    }
867    if self.contains_id(&new_relationship_id) {
868      return Err(SdkError::CommonError(format!(
869        "relationship id {new_relationship_id} already exists"
870      )));
871    }
872
873    let Some(index) = self.by_id.get(relationship_id).copied() else {
874      return Err(SdkError::CommonError(format!(
875        "relationship id {relationship_id} does not exist"
876      )));
877    };
878
879    self.relationships[index].id = new_relationship_id.into_boxed_str();
880    self.raw_bytes = None;
881    self.rebuild_index();
882    Ok(())
883  }
884
885  #[inline]
886  pub(crate) fn part_relationships(&self) -> impl Iterator<Item = &RelationshipInfo> {
887    self
888      .relationships
889      .iter()
890      .filter(|relationship| relationship.target_kind() == RelationshipTargetKind::InternalPart)
891  }
892
893  #[inline]
894  pub(crate) fn external_relationships(&self) -> impl Iterator<Item = &RelationshipInfo> {
895    self.relationships.iter().filter(|relationship| {
896      relationship.target_kind() == RelationshipTargetKind::External
897        && !super::relationship_type_matches_bytes(
898          relationship.relationship_type_bytes(),
899          super::REL_HYPERLINK,
900        )
901    })
902  }
903
904  #[inline]
905  pub(crate) fn hyperlink_relationships(&self) -> impl Iterator<Item = &RelationshipInfo> {
906    self.relationships.iter().filter(|relationship| {
907      super::relationship_type_matches_bytes(
908        relationship.relationship_type_bytes(),
909        super::REL_HYPERLINK,
910      )
911    })
912  }
913
914  #[inline]
915  pub(crate) fn data_part_reference_relationships(
916    &self,
917  ) -> impl Iterator<Item = &RelationshipInfo> {
918    self.relationships.iter().filter(|relationship| {
919      crate::common::is_data_part_reference_relationship_type_bytes(
920        relationship.relationship_type_bytes(),
921      )
922    })
923  }
924
925  #[inline]
926  pub(crate) fn first_target_part_by_relationship_type(
927    &self,
928    relationship_type: &str,
929  ) -> Option<PartId> {
930    self.relationships.iter().find_map(|relationship| {
931      super::relationship_type_matches_bytes(
932        relationship.relationship_type_bytes(),
933        relationship_type.as_bytes(),
934      )
935      .then(|| relationship.target_part_id())
936      .flatten()
937    })
938  }
939
940  pub(crate) fn to_relationships(&self) -> Relationships {
941    Relationships {
942      xmlns: vec![super::XmlNamespace::raw(
943        "",
944        "http://schemas.openxmlformats.org/package/2006/relationships",
945      )],
946      relationship: self
947        .relationships
948        .iter()
949        .map(RelationshipInfo::to_relationship)
950        .collect(),
951    }
952  }
953
954  pub(crate) fn to_bytes_cow(&self) -> Result<Cow<'_, [u8]>, SdkError> {
955    if let Some(bytes) = &self.raw_bytes {
956      return Ok(Cow::Borrowed(bytes));
957    }
958    let mut bytes = Vec::with_capacity(32);
959    crate::sdk::SdkType::write_to(&self.to_relationships(), &mut bytes)?;
960    Ok(Cow::Owned(bytes))
961  }
962
963  fn from_relationships(
964    relationships: Option<Relationships>,
965    source_path: &str,
966    by_path: &HashMap<Box<str>, PartId>,
967  ) -> Self {
968    let Some(relationships) = relationships else {
969      return Self::default();
970    };
971
972    let source_parent_path = super::parent_zip_path(source_path);
973    let mut set = Self {
974      relationships: Vec::with_capacity(relationships.relationship.len()),
975      by_id: HashMap::with_capacity(relationships.relationship.len()),
976      raw_bytes: None,
977      archive_entry_index: None,
978    };
979
980    for relationship in relationships.relationship {
981      let info = relationship_info(relationship, &source_parent_path, by_path);
982      set.push_relationship_unchecked(info);
983    }
984
985    set
986  }
987
988  fn from_raw_bytes(bytes: Box<[u8]>) -> Self {
989    Self {
990      relationships: Vec::new(),
991      by_id: HashMap::new(),
992      raw_bytes: Some(bytes),
993      archive_entry_index: None,
994    }
995  }
996
997  #[inline]
998  fn with_archive_entry(mut self, entry_index: usize) -> Self {
999    self.archive_entry_index = Some(entry_index);
1000    self
1001  }
1002
1003  #[inline]
1004  pub(crate) fn raw_archive_entry_index(&self) -> Option<usize> {
1005    self
1006      .raw_bytes
1007      .is_some()
1008      .then_some(self.archive_entry_index)
1009      .flatten()
1010  }
1011
1012  #[inline]
1013  fn original_archive_entry_index(&self) -> Option<usize> {
1014    self.archive_entry_index
1015  }
1016
1017  fn push_relationship(
1018    &mut self,
1019    relationship: RelationshipInfo,
1020  ) -> Result<&RelationshipInfo, SdkError> {
1021    if self.contains_id(relationship.id()) {
1022      return Err(SdkError::CommonError(format!(
1023        "relationship id {} already exists",
1024        relationship.id()
1025      )));
1026    }
1027    self.raw_bytes = None;
1028    self.push_relationship_unchecked(relationship);
1029    Ok(self.relationships.last().expect("pushed relationship"))
1030  }
1031
1032  fn push_relationship_unchecked(&mut self, relationship: RelationshipInfo) {
1033    let index = self.relationships.len();
1034    self.by_id.insert(relationship.id.clone(), index);
1035    self.relationships.push(relationship);
1036  }
1037
1038  fn rebuild_index(&mut self) {
1039    self.by_id.clear();
1040    self.by_id.reserve(self.relationships.len());
1041    for (index, relationship) in self.relationships.iter().enumerate() {
1042      self.by_id.insert(relationship.id.clone(), index);
1043    }
1044  }
1045}
1046
1047#[derive(Clone, Debug)]
1048pub(crate) struct StoredPart {
1049  path: Box<str>,
1050  content_type: Box<str>,
1051  relationship_type: Option<super::XmlRelationshipNamespaceUri>,
1052  kind: crate::parts::PartKind,
1053  relationships: RelationshipSet,
1054  data: StoredPartData,
1055  deleted: bool,
1056}
1057
1058impl StoredPart {
1059  #[inline]
1060  pub(crate) fn is_deleted(&self) -> bool {
1061    self.deleted
1062  }
1063
1064  #[inline]
1065  pub(crate) fn path(&self) -> &str {
1066    &self.path
1067  }
1068
1069  #[inline]
1070  pub(crate) fn content_type(&self) -> &str {
1071    &self.content_type
1072  }
1073
1074  #[inline]
1075  pub(crate) fn relationship_type(&self) -> Option<&str> {
1076    self
1077      .relationship_type
1078      .as_ref()
1079      .map(super::XmlRelationshipNamespaceUri::as_str)
1080  }
1081
1082  #[inline]
1083  pub(crate) fn relationship_type_bytes(&self) -> Option<&[u8]> {
1084    self
1085      .relationship_type
1086      .as_ref()
1087      .map(super::XmlRelationshipNamespaceUri::uri_bytes)
1088  }
1089
1090  #[inline]
1091  pub(crate) fn kind(&self) -> crate::parts::PartKind {
1092    self.kind
1093  }
1094
1095  #[inline]
1096  pub(crate) fn relationships(&self) -> &RelationshipSet {
1097    &self.relationships
1098  }
1099
1100  #[inline]
1101  pub(crate) fn relationships_mut(&mut self) -> &mut RelationshipSet {
1102    &mut self.relationships
1103  }
1104}
1105
1106#[doc(hidden)]
1107#[derive(Debug)]
1108pub struct SdkPackageStorage {
1109  id: PackageId,
1110  archive: Option<Arc<ArchiveBacking>>,
1111  archived_extra_entry_indices: Box<[usize]>,
1112  content_types: Types,
1113  content_types_archive_entry_index: Option<usize>,
1114  package_relationships: RelationshipSet,
1115  parts: Vec<StoredPart>,
1116  by_path: HashMap<Box<str>, PartId>,
1117  preferred_main_part_content_type: Option<&'static str>,
1118}
1119
1120impl Clone for SdkPackageStorage {
1121  fn clone(&self) -> Self {
1122    Self {
1123      id: PackageId::new(),
1124      archive: self.archive.clone(),
1125      archived_extra_entry_indices: self.archived_extra_entry_indices.clone(),
1126      content_types: self.content_types.clone(),
1127      content_types_archive_entry_index: self.content_types_archive_entry_index,
1128      package_relationships: self.package_relationships.clone(),
1129      parts: self.parts.clone(),
1130      by_path: self.by_path.clone(),
1131      preferred_main_part_content_type: self.preferred_main_part_content_type,
1132    }
1133  }
1134}
1135
1136impl SdkPackageStorage {
1137  pub(crate) fn create(preferred_main_part_content_type: Option<&'static str>) -> Self {
1138    Self {
1139      id: PackageId::new(),
1140      archive: None,
1141      archived_extra_entry_indices: Box::new([]),
1142      content_types: empty_content_types(),
1143      content_types_archive_entry_index: None,
1144      package_relationships: RelationshipSet::default(),
1145      parts: Vec::new(),
1146      by_path: HashMap::new(),
1147      preferred_main_part_content_type,
1148    }
1149  }
1150
1151  pub(crate) fn open<R: Read + Seek>(mut reader: R) -> Result<Self, SdkError> {
1152    let length = reader.seek(SeekFrom::End(0))?;
1153    reader.seek(SeekFrom::Start(0))?;
1154    let mut bytes = Vec::new();
1155    let capacity = usize::try_from(length)
1156      .map_err(|_| SdkError::CommonError("package is too large for this platform".to_string()))?;
1157    bytes.try_reserve_exact(capacity).map_err(|error| {
1158      SdkError::CommonError(format!(
1159        "cannot allocate {capacity} bytes for package input: {error}"
1160      ))
1161    })?;
1162    reader.read_to_end(&mut bytes)?;
1163    Self::open_memory(bytes.into())
1164  }
1165
1166  pub(crate) fn open_file(path: &Path) -> Result<Self, SdkError> {
1167    #[cfg(any(unix, windows))]
1168    {
1169      let file = open_package_file(path)?;
1170      let reader = PositionedFileReader::new(file)?;
1171      let archive = zip::ZipArchive::new(ArchiveReader::File(reader))?;
1172      Self::open_archive(archive)
1173    }
1174    #[cfg(not(any(unix, windows)))]
1175    {
1176      Self::open_memory(std::fs::read(path)?.into())
1177    }
1178  }
1179
1180  fn open_memory(bytes: Bytes) -> Result<Self, SdkError> {
1181    let archive = zip::ZipArchive::new(ArchiveReader::Memory(Cursor::new(bytes)))?;
1182    Self::open_archive(archive)
1183  }
1184
1185  fn open_archive(mut archive: zip::ZipArchive<ArchiveReader>) -> Result<Self, SdkError> {
1186    let opened = read_archive_model(&mut archive)?;
1187    let OpenedArchiveModel {
1188      content_types,
1189      content_types_archive_entry_index,
1190      mut raw_parts,
1191      package_relationships,
1192      part_relationships,
1193    } = opened;
1194    let mut claimed_entry_indices = vec![false; archive.len()];
1195    claimed_entry_indices[content_types_archive_entry_index] = true;
1196    for raw_part in &raw_parts {
1197      claimed_entry_indices[raw_part.entry_index] = true;
1198    }
1199    for entry in part_relationships.iter().flatten() {
1200      claimed_entry_indices[entry.entry_index] = true;
1201    }
1202    if let Some(entry) = &package_relationships {
1203      claimed_entry_indices[entry.entry_index] = true;
1204    }
1205    let archived_extra_entry_indices = (0..archive.len())
1206      .filter(|entry_index| !claimed_entry_indices[*entry_index])
1207      .collect::<Box<[_]>>();
1208    let mut by_path = HashMap::with_capacity(raw_parts.len());
1209
1210    for (index, raw_part) in raw_parts.iter().enumerate() {
1211      by_path.insert(raw_part.path.clone(), PartId::from_index(index));
1212    }
1213
1214    let package_relationships = package_relationships
1215      .map(|loaded| {
1216        RelationshipSet::from_relationships(loaded.relationships, "", &by_path)
1217          .with_archive_entry(loaded.entry_index)
1218      })
1219      .unwrap_or_default();
1220
1221    let part_relationships = part_relationships
1222      .into_iter()
1223      .zip(&raw_parts)
1224      .map(|(loaded, raw_part)| {
1225        loaded
1226          .map(|loaded| {
1227            match loaded.relationships {
1228              Some(relationships) => {
1229                RelationshipSet::from_relationships(Some(relationships), &raw_part.path, &by_path)
1230              }
1231              None => RelationshipSet::from_raw_bytes(loaded.bytes.into_boxed_slice()),
1232            }
1233            .with_archive_entry(loaded.entry_index)
1234          })
1235          .unwrap_or_default()
1236      })
1237      .collect::<Vec<_>>();
1238
1239    let relationship_types =
1240      relationship_types_by_part(&package_relationships, &part_relationships)?;
1241
1242    let mut parts = Vec::with_capacity(raw_parts.len());
1243    for (index, (raw_part, relationships)) in
1244      raw_parts.drain(..).zip(part_relationships).enumerate()
1245    {
1246      let relationship_type = relationship_types[index].clone();
1247      let kind = crate::parts::PartKind::classify(
1248        relationship_type
1249          .as_ref()
1250          .map(super::XmlRelationshipNamespaceUri::uri_bytes),
1251        raw_part.content_type.as_bytes(),
1252        &raw_part.path,
1253      );
1254      parts.push(StoredPart {
1255        path: raw_part.path,
1256        content_type: raw_part.content_type,
1257        relationship_type,
1258        kind,
1259        relationships,
1260        data: StoredPartData::Archived {
1261          entry_index: raw_part.entry_index,
1262          bytes: OnceLock::new(),
1263        },
1264        deleted: false,
1265      });
1266    }
1267
1268    let archive = Arc::new(ArchiveBacking { archive });
1269
1270    Ok(Self {
1271      id: PackageId::new(),
1272      archive: Some(archive),
1273      archived_extra_entry_indices,
1274      content_types,
1275      content_types_archive_entry_index: Some(content_types_archive_entry_index),
1276      package_relationships,
1277      parts,
1278      by_path,
1279      preferred_main_part_content_type: None,
1280    })
1281  }
1282
1283  #[cfg(feature = "flat-opc")]
1284  pub(crate) fn open_flat_opc<R: std::io::BufRead>(reader: R) -> Result<Self, SdkError> {
1285    let mut flat_parts = read_flat_opc_parts(reader)?;
1286    flat_parts.sort_by(|left, right| left.path.cmp(&right.path));
1287
1288    let mut raw_parts = Vec::new();
1289    let mut relationship_parts = HashMap::new();
1290    for flat_part in flat_parts {
1291      if is_relationships_part_path(&flat_part.path) {
1292        relationship_parts.insert(flat_part.path, flat_part.bytes);
1293      } else {
1294        raw_parts.push(RawPart {
1295          path: flat_part.path.into_boxed_str(),
1296          content_type: flat_part.content_type.into_boxed_str(),
1297          entry_index: usize::MAX,
1298          bytes: flat_part.bytes,
1299        });
1300      }
1301    }
1302
1303    let mut by_path = HashMap::with_capacity(raw_parts.len());
1304    for (index, raw_part) in raw_parts.iter().enumerate() {
1305      by_path.insert(raw_part.path.clone(), PartId::from_index(index));
1306    }
1307
1308    let content_types = content_types_from_raw_parts(&raw_parts);
1309    let package_relationships = relationships_from_flat_opc_part(
1310      relationship_parts.remove("_rels/.rels").as_deref(),
1311      "",
1312      &by_path,
1313    )?;
1314
1315    let mut part_relationships = Vec::with_capacity(raw_parts.len());
1316    for raw_part in &raw_parts {
1317      let rels_path = part_relationships_path(&raw_part.path);
1318      part_relationships.push(relationships_from_flat_opc_part(
1319        relationship_parts.remove(&rels_path).as_deref(),
1320        &raw_part.path,
1321        &by_path,
1322      )?);
1323    }
1324
1325    let relationship_types =
1326      relationship_types_by_part(&package_relationships, &part_relationships)?;
1327
1328    let mut parts = Vec::with_capacity(raw_parts.len());
1329    for (index, (raw_part, relationships)) in
1330      raw_parts.into_iter().zip(part_relationships).enumerate()
1331    {
1332      let relationship_type = relationship_types[index].clone();
1333      let kind = crate::parts::PartKind::classify(
1334        relationship_type
1335          .as_ref()
1336          .map(super::XmlRelationshipNamespaceUri::uri_bytes),
1337        raw_part.content_type.as_bytes(),
1338        &raw_part.path,
1339      );
1340      parts.push(StoredPart {
1341        path: raw_part.path,
1342        content_type: raw_part.content_type,
1343        relationship_type,
1344        kind,
1345        relationships,
1346        data: StoredPartData::Owned {
1347          bytes: raw_part.bytes.into(),
1348          original_entry_index: None,
1349        },
1350        deleted: false,
1351      });
1352    }
1353
1354    Ok(Self {
1355      id: PackageId::new(),
1356      archive: None,
1357      archived_extra_entry_indices: Box::new([]),
1358      content_types,
1359      content_types_archive_entry_index: None,
1360      package_relationships,
1361      parts,
1362      by_path,
1363      preferred_main_part_content_type: None,
1364    })
1365  }
1366
1367  #[cfg(feature = "flat-opc")]
1368  pub(crate) fn write_flat_opc<W, F>(
1369    &self,
1370    writer: &mut W,
1371    mut part_data: F,
1372  ) -> Result<(), SdkError>
1373  where
1374    W: std::io::Write,
1375    F: FnMut(PartId, &StoredPart) -> Result<Vec<u8>, SdkError>,
1376  {
1377    writer.write_all(br#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>"#)?;
1378    writer.write_all(b"\n<pkg:package")?;
1379    writer.write_all(b" xmlns:pkg=\"")?;
1380    writer.write_all(FLAT_OPC_PACKAGE_NS.as_bytes())?;
1381    writer.write_all(b"\"")?;
1382    writer.write_all(b">\n")?;
1383
1384    if !self.package_relationships.is_empty() {
1385      let bytes = self.package_relationships.to_bytes_cow()?;
1386      write_flat_opc_xml_part(
1387        writer,
1388        "/_rels/.rels",
1389        RELATIONSHIP_CONTENT_TYPE,
1390        bytes.as_ref(),
1391      )?;
1392    }
1393
1394    let alt_chunk_part_ids = self.alt_chunk_part_ids();
1395    for (index, part) in self.parts.iter().enumerate() {
1396      if part.is_deleted() {
1397        continue;
1398      }
1399
1400      let part_id = PartId::from_index(index);
1401      let data = part_data(part_id, part)?;
1402      let part_name = format!("/{}", part.path());
1403      if flat_opc_part_is_xml(part, part_id, &alt_chunk_part_ids) {
1404        write_flat_opc_xml_part(writer, &part_name, part.content_type(), &data)?;
1405      } else {
1406        write_flat_opc_binary_part(writer, &part_name, part.content_type(), &data)?;
1407      }
1408
1409      if !part.relationships().is_empty() {
1410        let bytes = part.relationships().to_bytes_cow()?;
1411        let rels_name = format!("/{}", part_relationships_path(part.path()));
1412        write_flat_opc_xml_part(
1413          writer,
1414          &rels_name,
1415          RELATIONSHIP_CONTENT_TYPE,
1416          bytes.as_ref(),
1417        )?;
1418      }
1419    }
1420
1421    writer.write_all(b"</pkg:package>")?;
1422    Ok(())
1423  }
1424
1425  #[inline]
1426  pub(crate) fn id(&self) -> PackageId {
1427    self.id
1428  }
1429
1430  #[inline]
1431  pub(crate) fn content_types(&self) -> &Types {
1432    &self.content_types
1433  }
1434
1435  pub(crate) fn package_save_entries(&self) -> Vec<PackageSaveEntry> {
1436    let mut entries = Vec::with_capacity(
1437      self.parts.len().saturating_mul(2) + self.archived_extra_entry_indices.len() + 2,
1438    );
1439    let mut sequence = 0usize;
1440    let mut push = |original_entry_index: Option<usize>, entry: PackageSaveEntry| {
1441      entries.push((original_entry_index.unwrap_or(usize::MAX), sequence, entry));
1442      sequence += 1;
1443    };
1444
1445    push(
1446      self.content_types_archive_entry_index,
1447      PackageSaveEntry::ContentTypes,
1448    );
1449    if !self.package_relationships.is_empty() {
1450      push(
1451        self.package_relationships.original_archive_entry_index(),
1452        PackageSaveEntry::PackageRelationships,
1453      );
1454    }
1455    for (index, part) in self.parts.iter().enumerate() {
1456      if part.is_deleted() {
1457        continue;
1458      }
1459      let part_id = PartId::from_index(index);
1460      if !part.relationships().is_empty() {
1461        push(
1462          part.relationships().original_archive_entry_index(),
1463          PackageSaveEntry::PartRelationships(part_id),
1464        );
1465      }
1466      push(
1467        part.data.original_entry_index(),
1468        PackageSaveEntry::Part(part_id),
1469      );
1470    }
1471    for &entry_index in &self.archived_extra_entry_indices {
1472      push(
1473        Some(entry_index),
1474        PackageSaveEntry::ArchivedExtra(entry_index),
1475      );
1476    }
1477
1478    entries.sort_unstable_by_key(|(entry_index, sequence, _)| (*entry_index, *sequence));
1479    entries.into_iter().map(|(_, _, entry)| entry).collect()
1480  }
1481
1482  pub(crate) fn configure_zip_writer<W: std::io::Write + std::io::Seek>(
1483    &self,
1484    writer: &mut zip::ZipWriter<W>,
1485  ) -> Result<(), SdkError> {
1486    if let Some(archive) = &self.archive {
1487      archive.configure_writer(writer)?;
1488    }
1489    Ok(())
1490  }
1491
1492  pub(crate) fn part_bytes(&self, part_id: PartId) -> Result<&[u8], SdkError> {
1493    let part = self.part(part_id).ok_or_else(|| {
1494      SdkError::CommonError(format!(
1495        "part id {part_id:?} is not present in package storage"
1496      ))
1497    })?;
1498    if let Some(bytes) = part.data.cached_bytes() {
1499      return Ok(bytes);
1500    }
1501
1502    let StoredPartData::Archived { entry_index, bytes } = &part.data else {
1503      unreachable!("owned part data always has cached bytes")
1504    };
1505    let archive = self.archive.as_ref().ok_or_else(|| {
1506      SdkError::CommonError("archived part has no package archive backing".to_string())
1507    })?;
1508    let loaded = archive.read_entry(*entry_index)?;
1509    let _ = bytes.set(loaded);
1510    Ok(
1511      bytes
1512        .get()
1513        .expect("part bytes were initialized or another thread initialized them"),
1514    )
1515  }
1516
1517  #[inline]
1518  pub(crate) fn discard_cached_part_bytes(&mut self, part_id: PartId) {
1519    if let Some(StoredPart {
1520      data: StoredPartData::Archived { bytes, .. },
1521      ..
1522    }) = self.part_mut(part_id)
1523    {
1524      let _ = bytes.take();
1525    }
1526  }
1527
1528  pub(crate) fn raw_copy_archive_entry<W: std::io::Write + std::io::Seek>(
1529    &self,
1530    entry_index: usize,
1531    target_name: &str,
1532    writer: &mut zip::ZipWriter<W>,
1533  ) -> Result<bool, SdkError> {
1534    let Some(archive) = &self.archive else {
1535      return Ok(false);
1536    };
1537    archive.raw_copy_entry(entry_index, target_name, writer)?;
1538    Ok(true)
1539  }
1540
1541  pub(crate) fn raw_copy_archived_extra<W: std::io::Write + std::io::Seek>(
1542    &self,
1543    entry_index: usize,
1544    writer: &mut zip::ZipWriter<W>,
1545  ) -> Result<(), SdkError> {
1546    let archive = self.archive.as_ref().ok_or_else(|| {
1547      SdkError::CommonError("archived ZIP entry has no package archive backing".to_string())
1548    })?;
1549    archive.raw_copy_entry_with_original_name(entry_index, writer)
1550  }
1551
1552  pub(crate) fn raw_copy_part<W: std::io::Write + std::io::Seek>(
1553    &self,
1554    part_id: PartId,
1555    writer: &mut zip::ZipWriter<W>,
1556  ) -> Result<bool, SdkError> {
1557    let Some(part) = self.part(part_id) else {
1558      return Ok(false);
1559    };
1560    let Some(entry_index) = part.data.unchanged_archive_entry_index() else {
1561      return Ok(false);
1562    };
1563    self.raw_copy_archive_entry(entry_index, part.path(), writer)
1564  }
1565
1566  #[inline]
1567  pub(crate) fn package_relationships(&self) -> &RelationshipSet {
1568    &self.package_relationships
1569  }
1570
1571  #[inline]
1572  pub(crate) fn package_relationships_mut(&mut self) -> &mut RelationshipSet {
1573    &mut self.package_relationships
1574  }
1575
1576  #[inline]
1577  pub(crate) fn parts(&self) -> &[StoredPart] {
1578    &self.parts
1579  }
1580
1581  #[inline]
1582  pub(crate) fn part(&self, part_id: PartId) -> Option<&StoredPart> {
1583    self
1584      .parts
1585      .get(part_id.index())
1586      .filter(|part| !part.is_deleted())
1587  }
1588
1589  #[inline]
1590  pub(crate) fn part_mut(&mut self, part_id: PartId) -> Option<&mut StoredPart> {
1591    self
1592      .parts
1593      .get_mut(part_id.index())
1594      .filter(|part| !part.is_deleted())
1595  }
1596
1597  #[inline]
1598  pub(crate) fn preferred_main_part_content_type(&self) -> Option<&str> {
1599    self.preferred_main_part_content_type
1600  }
1601
1602  #[inline]
1603  pub(crate) fn set_preferred_main_part_content_type(&mut self, content_type: &'static str) {
1604    self.preferred_main_part_content_type = Some(content_type);
1605  }
1606
1607  #[inline]
1608  pub(crate) fn media_data_parts(&self) -> impl Iterator<Item = (PartId, &StoredPart)> {
1609    self
1610      .parts
1611      .iter()
1612      .enumerate()
1613      .filter(|(_, part)| !part.is_deleted() && is_media_data_part(part))
1614      .map(|(index, part)| (PartId::from_index(index), part))
1615  }
1616
1617  #[inline]
1618  pub(crate) fn relationships(&self, part_id: PartId) -> Option<&RelationshipSet> {
1619    self.part(part_id).map(StoredPart::relationships)
1620  }
1621
1622  #[inline]
1623  pub(crate) fn relationships_mut(&mut self, part_id: PartId) -> Option<&mut RelationshipSet> {
1624    self.part_mut(part_id).map(StoredPart::relationships_mut)
1625  }
1626
1627  #[inline]
1628  pub(crate) fn target_part_id(
1629    &self,
1630    source_part_id: PartId,
1631    relationship_id: &str,
1632  ) -> Option<PartId> {
1633    self
1634      .relationships(source_part_id)?
1635      .get(relationship_id)?
1636      .target_part_id()
1637  }
1638
1639  pub(crate) fn delete_package_part(&mut self, relationship_id: &str) -> Result<bool, SdkError> {
1640    let Some(target_part_id) = self
1641      .package_relationships
1642      .get(relationship_id)
1643      .and_then(RelationshipInfo::target_part_id)
1644    else {
1645      return Ok(false);
1646    };
1647
1648    self.package_relationships.remove(relationship_id);
1649    if !self.is_part_reachable(target_part_id) {
1650      self.delete_unreachable_part_tree(target_part_id);
1651    }
1652    Ok(true)
1653  }
1654
1655  pub(crate) fn delete_child_part(
1656    &mut self,
1657    source_part_id: PartId,
1658    relationship_id: &str,
1659  ) -> Result<bool, SdkError> {
1660    let Some(target_part_id) = self.target_part_id(source_part_id, relationship_id) else {
1661      return Ok(false);
1662    };
1663
1664    let relationships = self.relationships_mut(source_part_id).ok_or_else(|| {
1665      SdkError::CommonError(format!(
1666        "part id {source_part_id:?} is not present in package storage"
1667      ))
1668    })?;
1669    relationships.remove(relationship_id);
1670    if !self.is_part_reachable(target_part_id) {
1671      self.delete_unreachable_part_tree(target_part_id);
1672    }
1673    Ok(true)
1674  }
1675
1676  pub(crate) fn add_package_relationship_to_part(
1677    &mut self,
1678    relationship_id: impl Into<String>,
1679    relationship_type: &str,
1680    target_part_id: PartId,
1681  ) -> Result<String, SdkError> {
1682    let relationship_id = relationship_id.into();
1683    if let Some(existing_relationship_id) =
1684      self.existing_relationship_id_for_target(None, target_part_id)
1685    {
1686      if existing_relationship_id == relationship_id {
1687        return Ok(existing_relationship_id);
1688      }
1689      return Err(SdkError::CommonError(format!(
1690        "part id {target_part_id:?} is already referenced by relationship id {existing_relationship_id}"
1691      )));
1692    }
1693
1694    let target = {
1695      let target_part = self.part(target_part_id).ok_or_else(|| {
1696        SdkError::CommonError(format!(
1697          "part id {target_part_id:?} is not present in package storage"
1698        ))
1699      })?;
1700      target_part.path().to_string()
1701    };
1702
1703    self.package_relationships.add_internal_part_relationship(
1704      relationship_id.clone(),
1705      relationship_type,
1706      target,
1707      target_part_id,
1708    )?;
1709    Ok(relationship_id)
1710  }
1711
1712  pub(crate) fn add_child_relationship_to_part(
1713    &mut self,
1714    source_part_id: PartId,
1715    relationship_id: impl Into<String>,
1716    relationship_type: &str,
1717    target_part_id: PartId,
1718  ) -> Result<String, SdkError> {
1719    let relationship_id = relationship_id.into();
1720    if let Some(existing_relationship_id) =
1721      self.existing_relationship_id_for_target(Some(source_part_id), target_part_id)
1722    {
1723      if existing_relationship_id == relationship_id {
1724        return Ok(existing_relationship_id);
1725      }
1726      return Err(SdkError::CommonError(format!(
1727        "part id {target_part_id:?} is already referenced by relationship id {existing_relationship_id}"
1728      )));
1729    }
1730
1731    let relationship_target = {
1732      let source_part_path = self
1733        .part(source_part_id)
1734        .ok_or_else(|| {
1735          SdkError::CommonError(format!(
1736            "part id {source_part_id:?} is not present in package storage"
1737          ))
1738        })?
1739        .path()
1740        .to_string();
1741      let target_part = self.part(target_part_id).ok_or_else(|| {
1742        SdkError::CommonError(format!(
1743          "part id {target_part_id:?} is not present in package storage"
1744        ))
1745      })?;
1746      relationship_target_from_source(&source_part_path, target_part.path())
1747    };
1748
1749    self
1750      .relationships_mut(source_part_id)
1751      .expect("source part was already resolved")
1752      .add_internal_part_relationship(
1753        relationship_id.clone(),
1754        relationship_type,
1755        relationship_target,
1756        target_part_id,
1757      )?;
1758    Ok(relationship_id)
1759  }
1760
1761  pub(crate) fn create_media_data_part(
1762    &mut self,
1763    content_type: impl Into<String>,
1764    extension: impl AsRef<str>,
1765  ) -> Result<PartId, SdkError> {
1766    let content_type = content_type.into();
1767    if content_type.is_empty() {
1768      return Err(SdkError::CommonError(
1769        "cannot add a media data part with an empty content type".to_string(),
1770      ));
1771    }
1772    let extension = normalized_part_extension(extension.as_ref());
1773    let path = self.next_data_part_path("media/mediadata", &extension);
1774    let part_id = self.push_part(path, &content_type, None);
1775    Ok(part_id)
1776  }
1777
1778  pub(crate) fn add_data_part_reference_relationship(
1779    &mut self,
1780    source_part_id: PartId,
1781    relationship_id: impl Into<String>,
1782    relationship_type: &str,
1783    target_part_id: PartId,
1784  ) -> Result<String, SdkError> {
1785    let relationship_id = relationship_id.into();
1786    let relationship_target = {
1787      let source_part_path = self
1788        .part(source_part_id)
1789        .ok_or_else(|| {
1790          SdkError::CommonError(format!(
1791            "part id {source_part_id:?} is not present in package storage"
1792          ))
1793        })?
1794        .path()
1795        .to_string();
1796      let target_part = self.part(target_part_id).ok_or_else(|| {
1797        SdkError::CommonError(format!(
1798          "part id {target_part_id:?} is not present in package storage"
1799        ))
1800      })?;
1801      relationship_target_from_source(&source_part_path, target_part.path())
1802    };
1803
1804    self
1805      .relationships_mut(source_part_id)
1806      .expect("source part was already resolved")
1807      .add_internal_part_relationship(
1808        relationship_id.clone(),
1809        relationship_type,
1810        relationship_target,
1811        target_part_id,
1812      )?;
1813    Ok(relationship_id)
1814  }
1815
1816  pub(crate) fn import_part_tree_from(
1817    &mut self,
1818    source: &Self,
1819    source_part_id: PartId,
1820    parent_part_id: Option<PartId>,
1821    relationship_id: impl Into<String>,
1822    relationship_type: &str,
1823    mut part_data: impl FnMut(PartId, &StoredPart) -> Result<Vec<u8>, SdkError>,
1824  ) -> Result<(PartId, usize), SdkError> {
1825    let relationship_id = relationship_id.into();
1826    source.part(source_part_id).ok_or_else(|| {
1827      SdkError::CommonError(format!(
1828        "source part id {source_part_id:?} is not present in package storage"
1829      ))
1830    })?;
1831
1832    match parent_part_id {
1833      Some(parent_part_id) => {
1834        let relationships = self.relationships(parent_part_id).ok_or_else(|| {
1835          SdkError::CommonError(format!(
1836            "part id {parent_part_id:?} is not present in package storage"
1837          ))
1838        })?;
1839        if relationships.contains_id(&relationship_id) {
1840          return Err(SdkError::CommonError(format!(
1841            "relationship id {relationship_id} already exists"
1842          )));
1843        }
1844      }
1845      None => {
1846        if self.package_relationships.contains_id(&relationship_id) {
1847          return Err(SdkError::CommonError(format!(
1848            "relationship id {relationship_id} already exists"
1849          )));
1850        }
1851      }
1852    }
1853
1854    let mut part_map = HashMap::new();
1855    let mut added_count = 0;
1856    let imported_part_id = self.import_part_tree_recursive(
1857      source,
1858      source_part_id,
1859      &mut part_map,
1860      &mut added_count,
1861      &mut part_data,
1862    )?;
1863    self.add_imported_part_relationship(
1864      parent_part_id,
1865      relationship_id,
1866      relationship_type,
1867      imported_part_id,
1868    )?;
1869    Ok((imported_part_id, added_count))
1870  }
1871
1872  #[inline]
1873  pub(crate) fn data_part_reference_relationships_to(
1874    &self,
1875    target_part_id: PartId,
1876  ) -> impl Iterator<Item = &RelationshipInfo> {
1877    std::iter::once(self.package_relationships())
1878      .chain(
1879        self
1880          .parts
1881          .iter()
1882          .filter(|part| !part.is_deleted())
1883          .map(StoredPart::relationships),
1884      )
1885      .flat_map(RelationshipSet::data_part_reference_relationships)
1886      .filter(move |relationship| relationship.target_part_id() == Some(target_part_id))
1887  }
1888
1889  pub(crate) fn delete_unused_media_data_parts(&mut self) -> usize {
1890    let unused_part_ids: Vec<_> = self
1891      .media_data_parts()
1892      .filter_map(|(part_id, _)| {
1893        self
1894          .data_part_reference_relationships_to(part_id)
1895          .next()
1896          .is_none()
1897          .then_some(part_id)
1898      })
1899      .collect();
1900
1901    let deleted_count = unused_part_ids.len();
1902    for part_id in unused_part_ids {
1903      let Some(part) = self.part(part_id) else {
1904        continue;
1905      };
1906      let path = part.path().to_string();
1907      if let Some(part) = self.parts.get_mut(part_id.index()) {
1908        part.deleted = true;
1909      }
1910      self.by_path.remove(path.as_str());
1911      self.remove_content_type_override(&path);
1912    }
1913    deleted_count
1914  }
1915
1916  fn import_part_tree_recursive(
1917    &mut self,
1918    source: &Self,
1919    source_part_id: PartId,
1920    part_map: &mut HashMap<PartId, PartId>,
1921    added_count: &mut usize,
1922    part_data: &mut impl FnMut(PartId, &StoredPart) -> Result<Vec<u8>, SdkError>,
1923  ) -> Result<PartId, SdkError> {
1924    if let Some(imported_part_id) = part_map.get(&source_part_id).copied() {
1925      return Ok(imported_part_id);
1926    }
1927
1928    let source_part = source.part(source_part_id).ok_or_else(|| {
1929      SdkError::CommonError(format!(
1930        "source part id {source_part_id:?} is not present in package storage"
1931      ))
1932    })?;
1933    let imported_path = self.unique_import_part_path(source_part.path());
1934    let imported_part_id = self.push_part(
1935      imported_path,
1936      source_part.content_type(),
1937      source_part.relationship_type(),
1938    );
1939    self.set_part_data(imported_part_id, part_data(source_part_id, source_part)?)?;
1940    *added_count += 1;
1941    part_map.insert(source_part_id, imported_part_id);
1942
1943    for relationship in source_part.relationships().iter() {
1944      if let Some(target_part_id) = relationship.target_part_id() {
1945        let imported_target_part_id = self.import_part_tree_recursive(
1946          source,
1947          target_part_id,
1948          part_map,
1949          added_count,
1950          part_data,
1951        )?;
1952        if relationship.is_reference_relationship() {
1953          self.add_data_part_reference_relationship(
1954            imported_part_id,
1955            relationship.id(),
1956            relationship.relationship_type(),
1957            imported_target_part_id,
1958          )?;
1959        } else {
1960          self.add_imported_part_relationship(
1961            Some(imported_part_id),
1962            relationship.id().to_string(),
1963            relationship.relationship_type(),
1964            imported_target_part_id,
1965          )?;
1966        }
1967      } else if relationship.is_reference_relationship()
1968        || relationship.target_kind() == RelationshipTargetKind::External
1969      {
1970        self
1971          .relationships_mut(imported_part_id)
1972          .expect("imported part was just created")
1973          .add_relationship_info(relationship.clone())?;
1974      }
1975    }
1976
1977    Ok(imported_part_id)
1978  }
1979
1980  fn add_imported_part_relationship(
1981    &mut self,
1982    parent_part_id: Option<PartId>,
1983    relationship_id: String,
1984    relationship_type: &str,
1985    target_part_id: PartId,
1986  ) -> Result<(), SdkError> {
1987    let target_part_path = self
1988      .part(target_part_id)
1989      .ok_or_else(|| {
1990        SdkError::CommonError(format!(
1991          "part id {target_part_id:?} is not present in package storage"
1992        ))
1993      })?
1994      .path()
1995      .to_string();
1996    let target = if let Some(parent_part_id) = parent_part_id {
1997      let parent_part_path = self
1998        .part(parent_part_id)
1999        .ok_or_else(|| {
2000          SdkError::CommonError(format!(
2001            "part id {parent_part_id:?} is not present in package storage"
2002          ))
2003        })?
2004        .path()
2005        .to_string();
2006      relationship_target_from_source(&parent_part_path, &target_part_path)
2007    } else {
2008      target_part_path
2009    };
2010    let relationships = match parent_part_id {
2011      Some(parent_part_id) => self.relationships_mut(parent_part_id).ok_or_else(|| {
2012        SdkError::CommonError(format!(
2013          "part id {parent_part_id:?} is not present in package storage"
2014        ))
2015      })?,
2016      None => self.package_relationships_mut(),
2017    };
2018    relationships.add_internal_part_relationship(
2019      relationship_id,
2020      relationship_type,
2021      target,
2022      target_part_id,
2023    )?;
2024    Ok(())
2025  }
2026
2027  pub(crate) fn add_child_part(
2028    &mut self,
2029    source_part_id: PartId,
2030    relationship_id: impl Into<String>,
2031    descriptor: NewPartDescriptor,
2032  ) -> Result<PartId, SdkError> {
2033    if descriptor.relationship_type.is_empty() {
2034      return Err(SdkError::CommonError(
2035        "cannot add a part with an empty relationship type".to_string(),
2036      ));
2037    }
2038    if descriptor.content_type.is_empty() {
2039      return Err(SdkError::CommonError(
2040        "cannot add a part with an empty content type".to_string(),
2041      ));
2042    }
2043    if descriptor.target_name.is_empty() {
2044      return Err(SdkError::CommonError(
2045        "cannot add a part with an empty target name".to_string(),
2046      ));
2047    }
2048
2049    let relationship_id = relationship_id.into();
2050    let source_part_path = self
2051      .part(source_part_id)
2052      .ok_or_else(|| {
2053        SdkError::CommonError(format!(
2054          "part id {source_part_id:?} is not present in package storage"
2055        ))
2056      })?
2057      .path()
2058      .to_string();
2059    if self
2060      .relationships(source_part_id)
2061      .is_some_and(|relationships| relationships.contains_id(&relationship_id))
2062    {
2063      return Err(SdkError::CommonError(format!(
2064        "relationship id {relationship_id} already exists"
2065      )));
2066    }
2067
2068    let child_path = self.next_child_part_path(
2069      &source_part_path,
2070      descriptor.path_prefix,
2071      descriptor.target_name,
2072      descriptor.extension.as_ref(),
2073      descriptor.content_type.as_ref(),
2074    );
2075    let relationship_target = relationship_target_from_source(&source_part_path, &child_path);
2076    let part_id = self.push_part(
2077      child_path,
2078      &descriptor.content_type,
2079      Some(descriptor.relationship_type.as_ref()),
2080    );
2081    self
2082      .relationships_mut(source_part_id)
2083      .expect("source part was already resolved")
2084      .add_internal_part_relationship(
2085        relationship_id,
2086        descriptor.relationship_type.as_ref(),
2087        relationship_target,
2088        part_id,
2089      )?;
2090    Ok(part_id)
2091  }
2092
2093  pub(crate) fn add_package_part(
2094    &mut self,
2095    relationship_id: impl Into<String>,
2096    descriptor: NewPartDescriptor,
2097    target_mode: NewPartTargetMode,
2098  ) -> Result<PartId, SdkError> {
2099    if descriptor.relationship_type.is_empty() {
2100      return Err(SdkError::CommonError(
2101        "cannot add a part with an empty relationship type".to_string(),
2102      ));
2103    }
2104    if descriptor.content_type.is_empty() {
2105      return Err(SdkError::CommonError(
2106        "cannot add a part with an empty content type".to_string(),
2107      ));
2108    }
2109    if descriptor.target_name.is_empty() {
2110      return Err(SdkError::CommonError(
2111        "cannot add a part with an empty target name".to_string(),
2112      ));
2113    }
2114
2115    let relationship_id = relationship_id.into();
2116    if self.package_relationships.contains_id(&relationship_id) {
2117      return Err(SdkError::CommonError(format!(
2118        "relationship id {relationship_id} already exists"
2119      )));
2120    }
2121
2122    let part_path = self.package_part_path(&descriptor, target_mode)?;
2123    let part_id = self.push_part(
2124      part_path.clone(),
2125      &descriptor.content_type,
2126      Some(descriptor.relationship_type.as_ref()),
2127    );
2128    self.package_relationships.add_internal_part_relationship(
2129      relationship_id,
2130      descriptor.relationship_type.as_ref(),
2131      part_path,
2132      part_id,
2133    )?;
2134    Ok(part_id)
2135  }
2136
2137  pub(crate) fn add_child_part_with_path(
2138    &mut self,
2139    source_part_id: PartId,
2140    relationship_id: impl Into<String>,
2141    descriptor: NewPartDescriptor,
2142    part_path: impl AsRef<str>,
2143  ) -> Result<PartId, SdkError> {
2144    if descriptor.relationship_type.is_empty() {
2145      return Err(SdkError::CommonError(
2146        "cannot add a part with an empty relationship type".to_string(),
2147      ));
2148    }
2149    if descriptor.content_type.is_empty() {
2150      return Err(SdkError::CommonError(
2151        "cannot add a part with an empty content type".to_string(),
2152      ));
2153    }
2154
2155    let relationship_id = relationship_id.into();
2156    let source_part_path = self
2157      .part(source_part_id)
2158      .ok_or_else(|| {
2159        SdkError::CommonError(format!(
2160          "part id {source_part_id:?} is not present in package storage"
2161        ))
2162      })?
2163      .path()
2164      .to_string();
2165    if self
2166      .relationships(source_part_id)
2167      .is_some_and(|relationships| relationships.contains_id(&relationship_id))
2168    {
2169      return Err(SdkError::CommonError(format!(
2170        "relationship id {relationship_id} already exists"
2171      )));
2172    }
2173
2174    let child_path =
2175      self.unique_new_part_path(part_path.as_ref(), descriptor.content_type.as_ref())?;
2176
2177    let relationship_target = relationship_target_from_source(&source_part_path, &child_path);
2178    let part_id = self.push_part(
2179      child_path,
2180      &descriptor.content_type,
2181      Some(descriptor.relationship_type.as_ref()),
2182    );
2183    self
2184      .relationships_mut(source_part_id)
2185      .expect("source part was already resolved")
2186      .add_internal_part_relationship(
2187        relationship_id,
2188        descriptor.relationship_type.as_ref(),
2189        relationship_target,
2190        part_id,
2191      )?;
2192    Ok(part_id)
2193  }
2194
2195  fn push_part(
2196    &mut self,
2197    path: String,
2198    content_type: &str,
2199    relationship_type: Option<&str>,
2200  ) -> PartId {
2201    let part_id = PartId::from_index(self.parts.len());
2202    let kind = crate::parts::PartKind::classify(
2203      relationship_type.map(str::as_bytes),
2204      content_type.as_bytes(),
2205      &path,
2206    );
2207    self.parts.push(StoredPart {
2208      path: path.clone().into_boxed_str(),
2209      content_type: content_type.into(),
2210      relationship_type: relationship_type.map(super::XmlRelationshipNamespaceUri::from_uri),
2211      kind,
2212      relationships: RelationshipSet::default(),
2213      data: StoredPartData::Owned {
2214        bytes: Bytes::new(),
2215        original_entry_index: None,
2216      },
2217      deleted: false,
2218    });
2219    self.by_path.insert(path.clone().into_boxed_str(), part_id);
2220    self.add_content_type_override(&path, content_type);
2221    part_id
2222  }
2223
2224  pub(crate) fn set_part_data(
2225    &mut self,
2226    part_id: PartId,
2227    data: impl Into<Vec<u8>>,
2228  ) -> Result<(), SdkError> {
2229    let part = self.part_mut(part_id).ok_or_else(|| {
2230      SdkError::CommonError(format!(
2231        "part id {part_id:?} is not present in package storage"
2232      ))
2233    })?;
2234    part.data.set_owned(data.into());
2235    Ok(())
2236  }
2237
2238  pub(crate) fn feed_part_data<R: Read>(
2239    &mut self,
2240    part_id: PartId,
2241    reader: &mut R,
2242  ) -> Result<(), SdkError> {
2243    let part = self.part_mut(part_id).ok_or_else(|| {
2244      SdkError::CommonError(format!(
2245        "part id {part_id:?} is not present in package storage"
2246      ))
2247    })?;
2248    let mut bytes = Vec::new();
2249    reader.read_to_end(&mut bytes)?;
2250    part.data.set_owned(bytes);
2251    Ok(())
2252  }
2253
2254  fn add_content_type_override(&mut self, path: &str, content_type: &str) {
2255    let part_name = format!("/{path}");
2256    if let Some(existing) = self
2257      .content_types
2258      .types_choice
2259      .iter_mut()
2260      .find_map(|child| match child {
2261        TypesChoice::Override(override_type) if override_type.part_name == part_name => {
2262          Some(override_type)
2263        }
2264        _ => None,
2265      })
2266    {
2267      existing.content_type = content_type.to_string();
2268      return;
2269    }
2270
2271    self.content_types.types_choice.push(TypesChoice::Override(
2272      crate::schemas::opc_content_types::Override {
2273        content_type: content_type.to_string(),
2274        part_name,
2275      },
2276    ));
2277  }
2278
2279  fn remove_content_type_override(&mut self, path: &str) {
2280    let part_name = format!("/{path}");
2281    self
2282      .content_types
2283      .types_choice
2284      .retain(|child| !matches!(child, TypesChoice::Override(override_type) if override_type.part_name == part_name));
2285  }
2286
2287  pub(crate) fn set_part_content_type(
2288    &mut self,
2289    part_id: PartId,
2290    content_type: impl Into<Box<str>>,
2291  ) -> Result<(), SdkError> {
2292    let content_type = content_type.into();
2293    if content_type.is_empty() {
2294      return Err(SdkError::CommonError(
2295        "cannot set a part to an empty content type".to_string(),
2296      ));
2297    }
2298    let path = {
2299      let part = self.part_mut(part_id).ok_or_else(|| {
2300        SdkError::CommonError(format!(
2301          "part id {part_id:?} is not present in package storage"
2302        ))
2303      })?;
2304      part.content_type = content_type.clone();
2305      part.kind = crate::parts::PartKind::classify(
2306        part.relationship_type_bytes(),
2307        content_type.as_bytes(),
2308        part.path(),
2309      );
2310      part.path().to_string()
2311    };
2312    self.add_content_type_override(&path, &content_type);
2313    Ok(())
2314  }
2315
2316  fn existing_relationship_id_for_target(
2317    &self,
2318    source_part_id: Option<PartId>,
2319    target_part_id: PartId,
2320  ) -> Option<String> {
2321    let relationships = match source_part_id {
2322      Some(source_part_id) => self.relationships(source_part_id)?,
2323      None => self.package_relationships(),
2324    };
2325    relationships.iter().find_map(|relationship| {
2326      (relationship.target_part_id() == Some(target_part_id)).then(|| relationship.id().to_string())
2327    })
2328  }
2329
2330  fn is_part_reachable(&self, target_part_id: PartId) -> bool {
2331    let mut visited = HashSet::new();
2332    let mut stack: Vec<_> = self
2333      .package_relationships
2334      .part_relationships()
2335      .filter_map(RelationshipInfo::target_part_id)
2336      .collect();
2337
2338    while let Some(part_id) = stack.pop() {
2339      if !visited.insert(part_id) {
2340        continue;
2341      }
2342      let Some(part) = self.part(part_id) else {
2343        continue;
2344      };
2345      if part_id == target_part_id {
2346        return true;
2347      }
2348      stack.extend(
2349        part
2350          .relationships()
2351          .part_relationships()
2352          .filter_map(RelationshipInfo::target_part_id),
2353      );
2354    }
2355
2356    false
2357  }
2358
2359  fn delete_unreachable_part_tree(&mut self, part_id: PartId) {
2360    let Some(part) = self.part(part_id) else {
2361      return;
2362    };
2363    let child_part_ids: Vec<_> = part
2364      .relationships()
2365      .part_relationships()
2366      .filter_map(RelationshipInfo::target_part_id)
2367      .collect();
2368    let path = part.path().to_string();
2369
2370    if let Some(part) = self.parts.get_mut(part_id.index()) {
2371      part.deleted = true;
2372    }
2373    self.by_path.remove(path.as_str());
2374    self.remove_content_type_override(&path);
2375
2376    for child_part_id in child_part_ids {
2377      if !self.is_part_reachable(child_part_id) {
2378        self.delete_unreachable_part_tree(child_part_id);
2379      }
2380    }
2381  }
2382
2383  fn unique_import_part_path(&self, source_path: &str) -> String {
2384    if !self.by_path.contains_key(source_path) {
2385      return source_path.to_string();
2386    }
2387
2388    let (base, extension) = match source_path.rsplit_once('.') {
2389      Some((base, extension)) => (base, format!(".{extension}")),
2390      None => (source_path, String::new()),
2391    };
2392    for index in 1.. {
2393      let candidate = format!("{base}_copy{index}{extension}");
2394      if !self.by_path.contains_key(candidate.as_str()) {
2395        return candidate;
2396      }
2397    }
2398    unreachable!("unbounded import path search should always find a candidate")
2399  }
2400
2401  fn next_child_part_path(
2402    &self,
2403    source_part_path: &str,
2404    path_prefix: &str,
2405    target_name: &str,
2406    extension: &str,
2407    content_type: &str,
2408  ) -> String {
2409    let directory_path = child_part_directory_path(source_part_path, path_prefix);
2410    let extension = if extension.is_empty() {
2411      ".xml"
2412    } else {
2413      extension
2414    };
2415    let extension = if extension.starts_with('.') {
2416      extension.to_string()
2417    } else {
2418      format!(".{extension}")
2419    };
2420
2421    let mut sequence = first_part_path_sequence(content_type);
2422    loop {
2423      let path = part_path_with_sequence(&directory_path, target_name, &extension, sequence);
2424      if !self.by_path.contains_key(path.as_str()) {
2425        return path;
2426      }
2427      sequence = next_part_path_sequence(sequence);
2428    }
2429  }
2430
2431  fn package_part_path(
2432    &self,
2433    descriptor: &NewPartDescriptor,
2434    target_mode: NewPartTargetMode,
2435  ) -> Result<String, SdkError> {
2436    let directory_path = package_part_directory_path(descriptor.path_prefix);
2437    let extension = normalized_part_extension(descriptor.extension.as_ref());
2438
2439    if matches!(target_mode, NewPartTargetMode::Fixed) {
2440      let path = if directory_path.is_empty() {
2441        format!("{}{}", descriptor.target_name, extension)
2442      } else {
2443        format!("{directory_path}{}{}", descriptor.target_name, extension)
2444      };
2445      if self.by_path.contains_key(path.as_str()) {
2446        return Err(SdkError::CommonError(format!(
2447          "part path {path} already exists"
2448        )));
2449      }
2450      return Ok(path);
2451    }
2452
2453    let mut sequence = first_part_path_sequence(descriptor.content_type.as_ref());
2454    loop {
2455      let path = part_path_with_sequence(
2456        &directory_path,
2457        descriptor.target_name,
2458        &extension,
2459        sequence,
2460      );
2461      if !self.by_path.contains_key(path.as_str()) {
2462        return Ok(path);
2463      }
2464      sequence = next_part_path_sequence(sequence);
2465    }
2466  }
2467
2468  fn next_data_part_path(&self, stem: &str, extension: &str) -> String {
2469    for index in 1.. {
2470      let path = format!("{stem}{index}{extension}");
2471      if !self.by_path.contains_key(path.as_str()) {
2472        return path;
2473      }
2474    }
2475
2476    unreachable!("usize iteration should always find a free data part path")
2477  }
2478
2479  fn unique_new_part_path(&self, path: &str, content_type: &str) -> Result<String, SdkError> {
2480    let normalized = normalized_new_part_path(path)?;
2481    let (stem, extension) = part_path_stem_and_extension(&normalized);
2482
2483    let mut sequence = if is_numbered_part_content_type(content_type) {
2484      1
2485    } else {
2486      0
2487    };
2488
2489    loop {
2490      let candidate = if sequence == 0 {
2491        normalized.clone()
2492      } else {
2493        format!("{stem}{sequence}{extension}")
2494      };
2495      if !self.by_path.contains_key(candidate.as_str()) {
2496        return Ok(candidate);
2497      }
2498      sequence = if sequence == 0 { 2 } else { sequence + 1 };
2499    }
2500  }
2501
2502  #[cfg(feature = "flat-opc")]
2503  fn alt_chunk_part_ids(&self) -> HashSet<PartId> {
2504    self
2505      .parts
2506      .iter()
2507      .filter(|part| !part.is_deleted())
2508      .flat_map(|part| part.relationships().iter())
2509      .chain(self.package_relationships().iter())
2510      .filter(|relationship| {
2511        super::relationship_type_matches_bytes(
2512          relationship.relationship_type_bytes(),
2513          super::REL_AF_CHUNK,
2514        )
2515      })
2516      .filter_map(RelationshipInfo::target_part_id)
2517      .collect()
2518  }
2519}
2520
2521struct RawPart {
2522  path: Box<str>,
2523  content_type: Box<str>,
2524  entry_index: usize,
2525  #[cfg(feature = "flat-opc")]
2526  bytes: Vec<u8>,
2527}
2528
2529struct LoadedRelationshipEntry {
2530  entry_index: usize,
2531  bytes: Vec<u8>,
2532  relationships: Option<Relationships>,
2533}
2534
2535struct OpenedArchiveModel {
2536  content_types: Types,
2537  content_types_archive_entry_index: usize,
2538  raw_parts: Vec<RawPart>,
2539  package_relationships: Option<LoadedRelationshipEntry>,
2540  part_relationships: Vec<Option<LoadedRelationshipEntry>>,
2541}
2542
2543struct ContentTypeResolver<'a> {
2544  overrides: HashMap<&'a str, &'a str>,
2545  defaults: HashMap<&'a str, &'a str>,
2546}
2547
2548impl<'a> ContentTypeResolver<'a> {
2549  fn new(content_types: &'a Types) -> Self {
2550    let mut overrides = HashMap::with_capacity(content_types.types_choice.len());
2551    let mut defaults = HashMap::with_capacity(content_types.types_choice.len());
2552
2553    for child in &content_types.types_choice {
2554      match child {
2555        TypesChoice::Override(override_type) => {
2556          overrides.insert(
2557            override_type.part_name.trim_start_matches('/'),
2558            override_type.content_type.as_str(),
2559          );
2560        }
2561        TypesChoice::Default(default_type) => {
2562          defaults.insert(
2563            default_type.extension.as_str(),
2564            default_type.content_type.as_str(),
2565          );
2566        }
2567      }
2568    }
2569
2570    Self {
2571      overrides,
2572      defaults,
2573    }
2574  }
2575
2576  fn content_type_for_part(&self, path: &str) -> Option<&'a str> {
2577    self.overrides.get(path).copied().or_else(|| {
2578      path
2579        .rsplit_once('.')
2580        .and_then(|(_, extension)| self.defaults.get(extension).copied())
2581    })
2582  }
2583}
2584
2585#[cfg(feature = "flat-opc")]
2586struct FlatOpcPart {
2587  path: String,
2588  content_type: String,
2589  bytes: Vec<u8>,
2590}
2591
2592#[cfg(feature = "flat-opc")]
2593fn content_types_from_raw_parts(raw_parts: &[RawPart]) -> Types {
2594  Types {
2595    types_choice: raw_parts
2596      .iter()
2597      .map(|part| {
2598        TypesChoice::Override(crate::schemas::opc_content_types::Override {
2599          content_type: part.content_type.to_string(),
2600          part_name: format!("/{}", part.path),
2601        })
2602      })
2603      .collect(),
2604    ..empty_content_types()
2605  }
2606}
2607
2608fn empty_content_types() -> Types {
2609  Types {
2610    xmlns: vec![super::XmlNamespace::raw(
2611      "",
2612      "http://schemas.openxmlformats.org/package/2006/content-types",
2613    )],
2614    types_choice: vec![TypesChoice::Default(
2615      crate::schemas::opc_content_types::Default {
2616        extension: "rels".to_string(),
2617        content_type: RELATIONSHIP_CONTENT_TYPE.to_string(),
2618      },
2619    )],
2620  }
2621}
2622
2623#[cfg(feature = "flat-opc")]
2624fn relationships_from_flat_opc_part(
2625  bytes: Option<&[u8]>,
2626  source_path: &str,
2627  by_path: &HashMap<Box<str>, PartId>,
2628) -> Result<RelationshipSet, SdkError> {
2629  let relationships = bytes
2630    .map(<Relationships as crate::sdk::SdkType>::from_bytes)
2631    .transpose()?;
2632  Ok(RelationshipSet::from_relationships(
2633    relationships,
2634    source_path,
2635    by_path,
2636  ))
2637}
2638
2639#[cfg(feature = "flat-opc")]
2640fn read_flat_opc_parts<R: std::io::BufRead>(reader: R) -> Result<Vec<FlatOpcPart>, SdkError> {
2641  let mut xml_reader = super::from_reader_inner(reader);
2642  let mut parts = Vec::new();
2643
2644  loop {
2645    match xml_reader.next_tag_event()? {
2646      super::PayloadEvent::Start(start, empty_tag)
2647        if qname_matches(start.name().as_ref(), b"part") =>
2648      {
2649        if empty_tag {
2650          return Err(SdkError::CommonError(
2651            "Flat OPC part must contain xmlData or binaryData".to_string(),
2652          ));
2653        }
2654        parts.push(read_flat_opc_part(&mut xml_reader, start)?);
2655      }
2656      super::PayloadEvent::Eof => break,
2657      _ => {}
2658    }
2659  }
2660
2661  Ok(parts)
2662}
2663
2664#[cfg(feature = "flat-opc")]
2665fn read_flat_opc_part<R: std::io::BufRead>(
2666  xml_reader: &mut super::xml::IoReader<R>,
2667  start: quick_xml::events::BytesStart<'static>,
2668) -> Result<FlatOpcPart, SdkError> {
2669  let decoder = xml_reader.decoder();
2670  let mut path = None;
2671  let mut content_type = None;
2672
2673  for attr in start.attributes() {
2674    let attr = attr?;
2675    if qname_matches(attr.key.as_ref(), b"name") {
2676      let value = attr.decoded_and_normalized_value(quick_xml::XmlVersion::Implicit1_0, decoder)?;
2677      path = Some(normalize_flat_opc_part_name(&value));
2678    } else if qname_matches(attr.key.as_ref(), b"contentType") {
2679      content_type = Some(
2680        attr
2681          .decoded_and_normalized_value(quick_xml::XmlVersion::Implicit1_0, decoder)?
2682          .into_owned(),
2683      );
2684    }
2685  }
2686
2687  let path =
2688    path.ok_or_else(|| SdkError::CommonError("Flat OPC part missing pkg:name".to_string()))?;
2689  let content_type = content_type
2690    .ok_or_else(|| SdkError::CommonError("Flat OPC part missing pkg:contentType".to_string()))?;
2691  let mut bytes = None;
2692
2693  loop {
2694    match xml_reader.next_tag_event()? {
2695      super::PayloadEvent::Start(start, empty_tag)
2696        if qname_matches(start.name().as_ref(), b"xmlData") =>
2697      {
2698        bytes = Some(read_flat_opc_xml_data(xml_reader, empty_tag)?);
2699      }
2700      super::PayloadEvent::Start(start, empty_tag)
2701        if qname_matches(start.name().as_ref(), b"binaryData") =>
2702      {
2703        bytes = Some(read_flat_opc_binary_data(xml_reader, empty_tag)?);
2704      }
2705      super::PayloadEvent::Start(_, true) => {}
2706      super::PayloadEvent::Start(_, false) => {
2707        return Err(SdkError::CommonError(
2708          "unsupported Flat OPC part child element".to_string(),
2709        ));
2710      }
2711      super::PayloadEvent::End(end) if qname_matches(end.name().as_ref(), b"part") => break,
2712      super::PayloadEvent::Eof => return Err(unexpected_eof("Flat OPC part")),
2713      _ => {}
2714    }
2715  }
2716
2717  let bytes = bytes.ok_or_else(|| {
2718    SdkError::CommonError("Flat OPC part must contain xmlData or binaryData".to_string())
2719  })?;
2720
2721  Ok(FlatOpcPart {
2722    path,
2723    content_type,
2724    bytes,
2725  })
2726}
2727
2728#[cfg(feature = "flat-opc")]
2729fn read_flat_opc_xml_data<R: std::io::BufRead>(
2730  xml_reader: &mut super::xml::IoReader<R>,
2731  empty_tag: bool,
2732) -> Result<Vec<u8>, SdkError> {
2733  if empty_tag {
2734    return Err(SdkError::CommonError(
2735      "Flat OPC xmlData must contain an XML root element".to_string(),
2736    ));
2737  }
2738
2739  let mut bytes = None;
2740  loop {
2741    match xml_reader.next_tag_event()? {
2742      super::PayloadEvent::Start(start, empty_tag) => {
2743        bytes = Some(super::read_outer_xml_io(xml_reader, start, empty_tag)?.into_bytes());
2744      }
2745      super::PayloadEvent::End(end) if qname_matches(end.name().as_ref(), b"xmlData") => break,
2746      super::PayloadEvent::Eof => return Err(unexpected_eof("Flat OPC xmlData")),
2747      _ => {}
2748    }
2749  }
2750
2751  bytes.ok_or_else(|| {
2752    SdkError::CommonError("Flat OPC xmlData must contain an XML root element".to_string())
2753  })
2754}
2755
2756#[cfg(feature = "flat-opc")]
2757fn read_flat_opc_binary_data<R: std::io::BufRead>(
2758  xml_reader: &mut super::xml::IoReader<R>,
2759  empty_tag: bool,
2760) -> Result<Vec<u8>, SdkError> {
2761  if empty_tag {
2762    return Ok(Vec::new());
2763  }
2764
2765  let mut text = String::new();
2766  loop {
2767    match xml_reader.next()? {
2768      super::xml::PayloadEvent::Text(event) => {
2769        text.push_str(event.xml10_content()?.as_ref());
2770      }
2771      super::xml::PayloadEvent::CData(event) => {
2772        text.push_str(event.xml10_content()?.as_ref());
2773      }
2774      super::xml::PayloadEvent::End(end) if qname_matches(end.name().as_ref(), b"binaryData") => {
2775        break;
2776      }
2777      super::xml::PayloadEvent::Eof => return Err(unexpected_eof("Flat OPC binaryData")),
2778      _ => {}
2779    }
2780  }
2781
2782  let compact: Vec<u8> = text
2783    .bytes()
2784    .filter(|byte| !byte.is_ascii_whitespace())
2785    .collect();
2786  base64::Engine::decode(&base64::engine::general_purpose::STANDARD, compact)
2787    .map_err(|err| SdkError::CommonError(format!("invalid Flat OPC binaryData: {err}")))
2788}
2789
2790#[cfg(feature = "flat-opc")]
2791fn write_flat_opc_xml_part<W: std::io::Write>(
2792  writer: &mut W,
2793  name: &str,
2794  content_type: &str,
2795  bytes: &[u8],
2796) -> Result<(), SdkError> {
2797  writer.write_all(b"  <pkg:part")?;
2798  writer.write_all(b" pkg:name=\"")?;
2799  super::write_escaped_str(writer, name)?;
2800  writer.write_all(b"\" pkg:contentType=\"")?;
2801  super::write_escaped_str(writer, content_type)?;
2802  writer.write_all(b"\"")?;
2803  writer.write_all(b">\n    <pkg:xmlData>")?;
2804  writer.write_all(root_xml_bytes(bytes)?.as_slice())?;
2805  writer.write_all(b"</pkg:xmlData>\n  </pkg:part>\n")?;
2806  Ok(())
2807}
2808
2809#[cfg(feature = "flat-opc")]
2810fn write_flat_opc_binary_part<W: std::io::Write>(
2811  writer: &mut W,
2812  name: &str,
2813  content_type: &str,
2814  bytes: &[u8],
2815) -> Result<(), SdkError> {
2816  writer.write_all(b"  <pkg:part")?;
2817  writer.write_all(b" pkg:name=\"")?;
2818  super::write_escaped_str(writer, name)?;
2819  writer.write_all(b"\" pkg:contentType=\"")?;
2820  super::write_escaped_str(writer, content_type)?;
2821  writer.write_all(b"\" pkg:compression=\"store\"")?;
2822  writer.write_all(b">\n    <pkg:binaryData>")?;
2823  let encoded = base64::Engine::encode(&base64::engine::general_purpose::STANDARD, bytes);
2824  writer.write_all(encoded.as_bytes())?;
2825  writer.write_all(b"</pkg:binaryData>\n  </pkg:part>\n")?;
2826  Ok(())
2827}
2828
2829#[cfg(feature = "flat-opc")]
2830fn root_xml_bytes(bytes: &[u8]) -> Result<Vec<u8>, SdkError> {
2831  let mut xml_reader = super::from_bytes_inner(bytes);
2832  loop {
2833    match xml_reader.next_tag_event()? {
2834      super::PayloadEvent::Start(start, empty_tag) => {
2835        return Ok(super::read_outer_xml_borrowed(&mut xml_reader, start, empty_tag)?.into_bytes());
2836      }
2837      super::PayloadEvent::Eof => return Err(unexpected_eof("Flat OPC XML part")),
2838      _ => {}
2839    }
2840  }
2841}
2842
2843#[cfg(feature = "flat-opc")]
2844fn flat_opc_part_is_xml(
2845  part: &StoredPart,
2846  part_id: PartId,
2847  alt_chunk_part_ids: &HashSet<PartId>,
2848) -> bool {
2849  part.content_type().ends_with("xml") && !alt_chunk_part_ids.contains(&part_id)
2850}
2851
2852#[cfg(feature = "flat-opc")]
2853fn qname_matches(qname: &[u8], local_name: &[u8]) -> bool {
2854  qname == local_name || qname.rsplit(|byte| *byte == b':').next() == Some(local_name)
2855}
2856
2857#[cfg(feature = "flat-opc")]
2858fn normalize_flat_opc_part_name(name: &str) -> String {
2859  resolve_zip_file_path(name.trim_start_matches('/'))
2860}
2861
2862fn child_part_directory_path(source_part_path: &str, path_prefix: &str) -> String {
2863  let source_parent_path = super::parent_zip_path(source_part_path);
2864  let mut path = if path_prefix.is_empty() || path_prefix == "." {
2865    source_parent_path
2866  } else if path_prefix == "../media"
2867    && matches!(source_parent_path.as_str(), "word/" | "ppt/" | "xl/")
2868  {
2869    format!("{source_parent_path}media")
2870  } else if path_prefix.starts_with('/') {
2871    path_prefix.to_string()
2872  } else {
2873    let mut path = String::with_capacity(source_parent_path.len() + path_prefix.len() + 1);
2874    path.push_str(&source_parent_path);
2875    path.push_str(path_prefix);
2876    path
2877  };
2878
2879  path = resolve_zip_file_path(&path);
2880  if !path.is_empty() && !path.ends_with('/') {
2881    path.push('/');
2882  }
2883  path
2884}
2885
2886fn package_part_directory_path(path_prefix: &str) -> String {
2887  if path_prefix.is_empty() || path_prefix == "." {
2888    return String::new();
2889  }
2890
2891  let mut path = resolve_zip_file_path(path_prefix);
2892  if !path.is_empty() && !path.ends_with('/') {
2893    path.push('/');
2894  }
2895  path
2896}
2897
2898fn normalized_part_extension(extension: &str) -> String {
2899  let extension = if extension.is_empty() {
2900    ".xml"
2901  } else {
2902    extension
2903  };
2904
2905  if extension.starts_with('.') {
2906    extension.to_string()
2907  } else {
2908    format!(".{extension}")
2909  }
2910}
2911
2912fn normalized_new_part_path(path: &str) -> Result<String, SdkError> {
2913  let path = path.trim_start_matches('/');
2914  if path.is_empty() {
2915    return Err(SdkError::CommonError("part path is empty".to_string()));
2916  }
2917  if path.split('/').any(|component| component == "..") {
2918    return Err(SdkError::CommonError(format!(
2919      "part path {path} is not allowed"
2920    )));
2921  }
2922
2923  let normalized = resolve_zip_file_path(path);
2924  if normalized.is_empty()
2925    || normalized.ends_with('/')
2926    || normalized == "[Content_Types].xml"
2927    || is_relationships_part_path(&normalized)
2928  {
2929    return Err(SdkError::CommonError(format!(
2930      "part path {path} is not allowed"
2931    )));
2932  }
2933
2934  Ok(normalized)
2935}
2936
2937fn part_path_stem_and_extension(path: &str) -> (&str, &str) {
2938  let file_name_start = path.rfind('/').map(|index| index + 1).unwrap_or(0);
2939  let file_name = &path[file_name_start..];
2940  if let Some(extension_start) = file_name.rfind('.') {
2941    let extension_start = file_name_start + extension_start;
2942    (&path[..extension_start], &path[extension_start..])
2943  } else {
2944    (path, "")
2945  }
2946}
2947
2948fn is_numbered_part_content_type(content_type: &str) -> bool {
2949  matches!(
2950    content_type,
2951    "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml"
2952      | "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml"
2953      | "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml"
2954      | "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml"
2955      | "application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml"
2956      | "application/vnd.openxmlformats-officedocument.drawing+xml"
2957      | "application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml"
2958      | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml"
2959      | "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml"
2960      | "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml"
2961      | "application/vnd.openxmlformats-officedocument.spreadsheetml.queryTable+xml"
2962      | "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionLog+xml"
2963      | "application/vnd.openxmlformats-officedocument.spreadsheetml.tableSingleCells+xml"
2964      | "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml"
2965      | "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"
2966      | "application/vnd.openxmlformats-officedocument.presentationml.comments+xml"
2967      | "application/vnd.openxmlformats-officedocument.presentationml.handoutMaster+xml"
2968      | "application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml"
2969      | "application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml"
2970      | "application/vnd.openxmlformats-officedocument.presentationml.slide+xml"
2971      | "application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml"
2972      | "application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml"
2973      | "application/vnd.openxmlformats-officedocument.presentationml.slideUpdateInfo+xml"
2974      | "application/vnd.openxmlformats-officedocument.presentationml.tags+xml"
2975      | "application/vnd.openxmlformats-officedocument.drawingml.chart+xml"
2976      | "application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml"
2977      | "application/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml"
2978      | "application/vnd.openxmlformats-officedocument.drawingml.diagramData+xml"
2979      | "application/vnd.openxmlformats-officedocument.drawingml.diagramLayout+xml"
2980      | "application/vnd.openxmlformats-officedocument.drawingml.diagramStyle+xml"
2981      | "application/vnd.openxmlformats-officedocument.theme+xml"
2982      | "application/vnd.openxmlformats-officedocument.themeOverride+xml"
2983      | "application/vnd.openxmlformats-officedocument.customXmlProperties+xml"
2984      | "application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings"
2985      | "application/vnd.openxmlformats-officedocument.wordprocessingml.printerSettings"
2986      | "application/vnd.openxmlformats-officedocument.presentationml.printerSettings"
2987  )
2988}
2989
2990fn first_part_path_sequence(content_type: &str) -> usize {
2991  usize::from(is_numbered_part_content_type(content_type))
2992}
2993
2994fn next_part_path_sequence(sequence: usize) -> usize {
2995  if sequence == 0 { 2 } else { sequence + 1 }
2996}
2997
2998fn part_path_with_sequence(
2999  directory_path: &str,
3000  target_name: &str,
3001  extension: &str,
3002  sequence: usize,
3003) -> String {
3004  match (directory_path.is_empty(), sequence) {
3005    (true, 0) => format!("{target_name}{extension}"),
3006    (true, sequence) => format!("{target_name}{sequence}{extension}"),
3007    (false, 0) => format!("{directory_path}{target_name}{extension}"),
3008    (false, sequence) => format!("{directory_path}{target_name}{sequence}{extension}"),
3009  }
3010}
3011
3012fn relationship_target_from_source(source_part_path: &str, child_part_path: &str) -> String {
3013  let source_parent_path = super::parent_zip_path(source_part_path);
3014  if let Some(relative) = child_part_path.strip_prefix(&source_parent_path) {
3015    relative.to_string()
3016  } else {
3017    format!("/{child_part_path}")
3018  }
3019}
3020
3021fn relationship_types_by_part(
3022  package_relationships: &RelationshipSet,
3023  part_relationships: &[RelationshipSet],
3024) -> Result<Vec<Option<super::XmlRelationshipNamespaceUri>>, SdkError> {
3025  let mut relationship_types: Vec<Option<super::XmlRelationshipNamespaceUri>> =
3026    vec![None; part_relationships.len()];
3027
3028  for relationship_set in std::iter::once(package_relationships).chain(part_relationships) {
3029    for relationship in relationship_set.iter() {
3030      let Some(part_id) = relationship.target_part_id() else {
3031        continue;
3032      };
3033      let slot = relationship_types.get_mut(part_id.index()).ok_or_else(|| {
3034        SdkError::CommonError(format!(
3035          "relationship target part id {part_id:?} is outside package storage"
3036        ))
3037      })?;
3038      if let Some(existing) = slot {
3039        if !relationship_types_are_compatible_bytes(
3040          existing.uri_bytes(),
3041          relationship.relationship_type_bytes(),
3042        ) {
3043          return Err(SdkError::CommonError(format!(
3044            "same part {:?} is referenced by different relationship types: {} and {}",
3045            part_id,
3046            existing.as_str(),
3047            relationship.relationship_type(),
3048          )));
3049        }
3050      } else {
3051        *slot = Some(super::XmlRelationshipNamespaceUri::from_uri(
3052          relationship.relationship_type(),
3053        ));
3054      }
3055    }
3056  }
3057
3058  Ok(relationship_types)
3059}
3060
3061fn relationship_types_are_compatible_bytes(left: &[u8], right: &[u8]) -> bool {
3062  left == right || data_part_reference_relationship_types_are_compatible_bytes(left, right)
3063}
3064
3065fn data_part_reference_relationship_types_are_compatible_bytes(left: &[u8], right: &[u8]) -> bool {
3066  super::is_data_part_reference_relationship_type_bytes(left)
3067    && super::is_data_part_reference_relationship_type_bytes(right)
3068}
3069
3070fn is_media_data_part(part: &StoredPart) -> bool {
3071  part
3072    .relationship_type_bytes()
3073    .is_some_and(super::is_data_part_reference_relationship_type_bytes)
3074    || media_data_part_content_type(part.content_type())
3075    || part
3076      .path()
3077      .rsplit('/')
3078      .next()
3079      .is_some_and(|file_name| file_name.starts_with("media"))
3080}
3081
3082fn media_data_part_content_type(content_type: &str) -> bool {
3083  content_type.starts_with("audio/") || content_type.starts_with("video/")
3084}
3085
3086fn read_archive_model<R: Read + Seek>(
3087  archive: &mut zip::ZipArchive<R>,
3088) -> Result<OpenedArchiveModel, SdkError> {
3089  let (content_types, content_types_archive_entry_index) = read_content_types(archive)?;
3090  let raw_parts = read_raw_parts(archive, &content_types)?;
3091  let (package_relationships, part_relationships) =
3092    read_required_relationship_entries(archive, &raw_parts)?;
3093
3094  Ok(OpenedArchiveModel {
3095    content_types,
3096    content_types_archive_entry_index,
3097    raw_parts,
3098    package_relationships,
3099    part_relationships,
3100  })
3101}
3102
3103#[derive(Clone, Copy)]
3104enum RelationshipEntryTarget {
3105  Package,
3106  Part(usize),
3107}
3108
3109fn read_required_relationship_entries<R: Read + Seek>(
3110  archive: &mut zip::ZipArchive<R>,
3111  raw_parts: &[RawPart],
3112) -> Result<
3113  (
3114    Option<LoadedRelationshipEntry>,
3115    Vec<Option<LoadedRelationshipEntry>>,
3116  ),
3117  SdkError,
3118> {
3119  let mut required = Vec::with_capacity(raw_parts.len() + 1);
3120  if let Some(entry_index) = archive.index_for_name("_rels/.rels") {
3121    required.push((entry_index, RelationshipEntryTarget::Package));
3122  }
3123  for (part_index, raw_part) in raw_parts.iter().enumerate() {
3124    let path = part_relationships_path(&raw_part.path);
3125    let entry_index = archive.index_for_name(&path).or_else(|| {
3126      lowercase_zip_filename(&path).and_then(|fallback| archive.index_for_name(&fallback))
3127    });
3128    if let Some(entry_index) = entry_index {
3129      required.push((entry_index, RelationshipEntryTarget::Part(part_index)));
3130    }
3131  }
3132  required.sort_unstable_by_key(|(entry_index, _)| *entry_index);
3133
3134  let mut package_relationships = None;
3135  let mut part_relationships = std::iter::repeat_with(|| None)
3136    .take(raw_parts.len())
3137    .collect::<Vec<_>>();
3138  for (entry_index, target) in required {
3139    let bytes = read_archive_entry_by_index(archive, entry_index)?;
3140    let relationships = <Relationships as crate::sdk::SdkType>::from_bytes(&bytes);
3141    let loaded = LoadedRelationshipEntry {
3142      entry_index,
3143      bytes,
3144      relationships: match target {
3145        RelationshipEntryTarget::Package => Some(relationships?),
3146        RelationshipEntryTarget::Part(_) => relationships.ok(),
3147      },
3148    };
3149    match target {
3150      RelationshipEntryTarget::Package => package_relationships = Some(loaded),
3151      RelationshipEntryTarget::Part(part_index) => {
3152        part_relationships[part_index] = Some(loaded);
3153      }
3154    }
3155  }
3156
3157  Ok((package_relationships, part_relationships))
3158}
3159
3160fn read_content_types<R: Read + Seek>(
3161  archive: &mut zip::ZipArchive<R>,
3162) -> Result<(Types, usize), SdkError> {
3163  let entry_index = archive
3164    .index_for_name("[Content_Types].xml")
3165    .ok_or(zip::result::ZipError::FileNotFound)?;
3166  let bytes = read_archive_entry_by_index(archive, entry_index)?;
3167  Ok((
3168    <Types as crate::sdk::SdkType>::from_bytes(&bytes)?,
3169    entry_index,
3170  ))
3171}
3172
3173fn read_raw_parts<R: Read + Seek>(
3174  archive: &mut zip::ZipArchive<R>,
3175  content_types: &Types,
3176) -> Result<Vec<RawPart>, SdkError> {
3177  let mut parts = Vec::new();
3178  let content_type_resolver = ContentTypeResolver::new(content_types);
3179
3180  for (index, entry_name) in archive.file_names().enumerate() {
3181    if entry_name.ends_with('/') {
3182      continue;
3183    }
3184
3185    let path = resolve_zip_file_path(entry_name);
3186    if path == "[Content_Types].xml" || is_relationships_part_path(&path) {
3187      continue;
3188    }
3189
3190    let content_type = content_type_resolver
3191      .content_type_for_part(&path)
3192      .unwrap_or_default();
3193    parts.push(RawPart {
3194      path: path.into_boxed_str(),
3195      content_type: content_type.into(),
3196      entry_index: index,
3197      #[cfg(feature = "flat-opc")]
3198      bytes: Vec::new(),
3199    });
3200  }
3201
3202  parts.sort_by(|left, right| left.path.cmp(&right.path));
3203  Ok(parts)
3204}
3205
3206fn read_archive_entry_by_index<R: Read + Seek>(
3207  archive: &mut zip::ZipArchive<R>,
3208  entry_index: usize,
3209) -> Result<Vec<u8>, SdkError> {
3210  let mut entry = archive.by_index(entry_index)?;
3211  let capacity = usize::try_from(entry.size()).map_err(|_| {
3212    SdkError::CommonError(format!(
3213      "ZIP entry {} is too large for this platform",
3214      entry.name()
3215    ))
3216  })?;
3217  let mut bytes = Vec::new();
3218  bytes.try_reserve_exact(capacity).map_err(|error| {
3219    SdkError::CommonError(format!(
3220      "cannot allocate {capacity} bytes for ZIP entry {}: {error}",
3221      entry.name()
3222    ))
3223  })?;
3224  entry.read_to_end(&mut bytes)?;
3225  Ok(bytes)
3226}
3227
3228fn lowercase_zip_filename(path: &str) -> Option<String> {
3229  let (parent_path, file_name) = path.rsplit_once('/')?;
3230  let lower_file_name = file_name.to_ascii_lowercase();
3231  if file_name == lower_file_name {
3232    return None;
3233  }
3234
3235  let mut fallback = String::with_capacity(path.len());
3236  fallback.push_str(parent_path);
3237  fallback.push('/');
3238  fallback.push_str(&lower_file_name);
3239  Some(fallback)
3240}
3241
3242fn relationship_info(
3243  relationship: OpcRelationship,
3244  source_parent_path: &str,
3245  by_path: &HashMap<Box<str>, PartId>,
3246) -> RelationshipInfo {
3247  let target_mode = relationship.target_mode;
3248  let effective_target_mode = target_mode.unwrap_or(TargetMode::Internal);
3249  let (target_kind, target_part_id) = if matches!(effective_target_mode, TargetMode::Internal) {
3250    if relationship.target.eq_ignore_ascii_case("NULL") {
3251      (RelationshipTargetKind::Null, None)
3252    } else {
3253      let target_path = resolve_zip_file_path(&resolve_relationship_target_path(
3254        source_parent_path,
3255        &relationship.target,
3256      ));
3257      match by_path.get(target_path.as_str()).copied() {
3258        Some(part_id) => (RelationshipTargetKind::InternalPart, Some(part_id)),
3259        None => (RelationshipTargetKind::Missing, None),
3260      }
3261    }
3262  } else {
3263    (RelationshipTargetKind::External, None)
3264  };
3265
3266  RelationshipInfo {
3267    id: relationship.id.into_boxed_str(),
3268    relationship_type: super::XmlRelationshipNamespaceUri::from_uri(&relationship.r#type),
3269    target: relationship.target.into_boxed_str(),
3270    target_mode,
3271    target_kind,
3272    target_part_id,
3273  }
3274}
3275
3276fn is_relationships_part_path(path: &str) -> bool {
3277  path == "_rels/.rels" || path.contains("/_rels/") && path.ends_with(".rels")
3278}
3279
3280#[cfg(test)]
3281mod tests {
3282  use super::*;
3283  use std::io::{Cursor, Write};
3284
3285  #[test]
3286  fn storage_resolves_package_relationship_target_part_id() {
3287    let mut buffer = Cursor::new(Vec::new());
3288    {
3289      let mut zip = zip::ZipWriter::new(&mut buffer);
3290      let options = zip::write::SimpleFileOptions::default();
3291
3292      zip.start_file("[Content_Types].xml", options).unwrap();
3293      zip.write_all(
3294        br#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3295<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
3296  <Default Extension="xml" ContentType="application/xml"/>
3297  <Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
3298</Types>"#,
3299      )
3300      .unwrap();
3301
3302      zip.add_directory("_rels", options).unwrap();
3303      zip.start_file("_rels/.rels", options).unwrap();
3304      zip.write_all(
3305        br#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3306<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
3307  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
3308</Relationships>"#,
3309      )
3310      .unwrap();
3311
3312      zip.add_directory("word", options).unwrap();
3313      zip.start_file("word/document.xml", options).unwrap();
3314      zip.write_all(br#"<w:document xmlns:w="w"/>"#).unwrap();
3315      zip.finish().unwrap();
3316    }
3317
3318    buffer.set_position(0);
3319    let storage = SdkPackageStorage::open(buffer).unwrap();
3320    let relationship = storage.package_relationships().get("rId1").unwrap();
3321    let part_id = relationship.target_part_id().unwrap();
3322    let part = storage.part(part_id).unwrap();
3323
3324    assert_eq!(part.path(), "word/document.xml");
3325    assert_eq!(
3326      part.relationship_type(),
3327      Some("http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument")
3328    );
3329    assert_eq!(
3330      relationship.target_kind(),
3331      RelationshipTargetKind::InternalPart
3332    );
3333    assert!(matches!(
3334      &storage.parts[part_id.index()].data,
3335      StoredPartData::Archived { bytes, .. } if bytes.get().is_none()
3336    ));
3337    assert_eq!(
3338      storage.package_save_entries(),
3339      vec![
3340        PackageSaveEntry::ContentTypes,
3341        PackageSaveEntry::ArchivedExtra(1),
3342        PackageSaveEntry::PackageRelationships,
3343        PackageSaveEntry::ArchivedExtra(3),
3344        PackageSaveEntry::Part(part_id),
3345      ]
3346    );
3347    assert_eq!(
3348      storage.part_bytes(part_id).unwrap(),
3349      br#"<w:document xmlns:w="w"/>"#
3350    );
3351    assert!(matches!(
3352      &storage.parts[part_id.index()].data,
3353      StoredPartData::Archived { bytes, .. } if bytes.get().is_some()
3354    ));
3355  }
3356
3357  #[test]
3358  fn storage_reads_part_relationships_with_lowercase_filename_fallback() {
3359    let mut buffer = Cursor::new(Vec::new());
3360    {
3361      let mut zip = zip::ZipWriter::new(&mut buffer);
3362      let options = zip::write::SimpleFileOptions::default();
3363
3364      zip.start_file("[Content_Types].xml", options).unwrap();
3365      zip.write_all(
3366        br#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3367<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
3368  <Default Extension="xml" ContentType="application/xml"/>
3369  <Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
3370  <Override PartName="/xl/worksheets/Sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
3371  <Override PartName="/xl/drawings/drawing1.xml" ContentType="application/vnd.openxmlformats-officedocument.drawing+xml"/>
3372</Types>"#,
3373      )
3374      .unwrap();
3375
3376      zip.add_directory("_rels", options).unwrap();
3377      zip.start_file("_rels/.rels", options).unwrap();
3378      zip.write_all(
3379        br#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3380<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
3381  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
3382</Relationships>"#,
3383      )
3384      .unwrap();
3385
3386      zip.add_directory("xl/_rels", options).unwrap();
3387      zip
3388        .start_file("xl/_rels/workbook.xml.rels", options)
3389        .unwrap();
3390      zip.write_all(
3391        br#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3392<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
3393  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/Sheet1.xml"/>
3394</Relationships>"#,
3395      )
3396      .unwrap();
3397
3398      zip.add_directory("xl/worksheets/_rels", options).unwrap();
3399      zip
3400        .start_file("xl/worksheets/_rels/sheet1.xml.rels", options)
3401        .unwrap();
3402      zip.write_all(
3403        br#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3404<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
3405  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing" Target="../drawings/drawing1.xml"/>
3406</Relationships>"#,
3407      )
3408      .unwrap();
3409
3410      zip.add_directory("xl", options).unwrap();
3411      zip.start_file("xl/workbook.xml", options).unwrap();
3412      zip.write_all(br#"<workbook/>"#).unwrap();
3413      zip.add_directory("xl/worksheets", options).unwrap();
3414      zip.start_file("xl/worksheets/Sheet1.xml", options).unwrap();
3415      zip.write_all(br#"<worksheet/>"#).unwrap();
3416      zip.add_directory("xl/drawings", options).unwrap();
3417      zip.start_file("xl/drawings/drawing1.xml", options).unwrap();
3418      zip.write_all(br#"<xdr:wsDr xmlns:xdr="xdr"/>"#).unwrap();
3419      zip.finish().unwrap();
3420    }
3421
3422    buffer.set_position(0);
3423    let storage = SdkPackageStorage::open(buffer).unwrap();
3424    let sheet_part_id = storage
3425      .package_relationships()
3426      .get("rId1")
3427      .and_then(RelationshipInfo::target_part_id)
3428      .and_then(|workbook_part_id| storage.relationships(workbook_part_id))
3429      .and_then(|relationships| relationships.get("rId1"))
3430      .and_then(RelationshipInfo::target_part_id)
3431      .unwrap();
3432    let drawing_relationship = storage
3433      .relationships(sheet_part_id)
3434      .unwrap()
3435      .get("rId1")
3436      .unwrap();
3437
3438    assert_eq!(
3439      drawing_relationship.target_kind(),
3440      RelationshipTargetKind::InternalPart
3441    );
3442    let drawing_part = storage
3443      .part(drawing_relationship.target_part_id().unwrap())
3444      .unwrap();
3445    assert_eq!(drawing_part.path(), "xl/drawings/drawing1.xml");
3446  }
3447}