#[cfg(feature = "parts")]
pub enum OptionalPartKind {}
#[cfg(feature = "parts")]
pub enum RequiredPartKind {}
#[cfg(feature = "parts")]
pub enum RepeatedPartKind {}
#[cfg(feature = "parts")]
const OLE_COMPOUND_FILE_SIGNATURE: [u8; 8] = [0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1];
#[cfg(feature = "parts")]
const ENCRYPTED_PACKAGE_CONTENT_TYPE: &str =
"application/vnd.openxmlformats-officedocument.encrypted-package";
#[cfg(feature = "parts")]
pub fn is_encrypted_office_file<R>(reader: &mut R) -> Result<bool, crate::common::SdkError>
where
R: std::io::Read + std::io::Seek,
{
let original_position = reader.stream_position()?;
let result = is_encrypted_office_file_inner(reader);
reader.seek(std::io::SeekFrom::Start(original_position))?;
result
}
#[cfg(feature = "parts")]
pub fn is_encrypted_office_file_path(
path: impl AsRef<std::path::Path>,
) -> Result<bool, crate::common::SdkError> {
let mut file = std::fs::File::open(path)?;
is_encrypted_office_file(&mut file)
}
#[cfg(feature = "parts")]
fn is_encrypted_office_file_inner<R>(reader: &mut R) -> Result<bool, crate::common::SdkError>
where
R: std::io::Read + std::io::Seek,
{
let mut header = [0; 8];
reader.seek(std::io::SeekFrom::Start(0))?;
let read = std::io::Read::read(reader, &mut header)?;
if read == header.len() && header == OLE_COMPOUND_FILE_SIGNATURE {
return Ok(true);
}
reader.seek(std::io::SeekFrom::Start(0))?;
let Ok(mut archive) = zip::ZipArchive::new(reader) else {
return Ok(false);
};
let Ok(mut content_types) = archive.by_name("[Content_Types].xml") else {
return Ok(false);
};
let mut content_types_xml = String::new();
if std::io::Read::read_to_string(&mut content_types, &mut content_types_xml).is_err() {
return Ok(false);
}
Ok(
content_types_xml
.to_ascii_lowercase()
.contains(ENCRYPTED_PACKAGE_CONTENT_TYPE),
)
}
#[cfg(feature = "parts")]
pub struct PartChild<T, C>(std::marker::PhantomData<(T, C)>);
#[cfg(feature = "parts")]
pub type OptionalPart<T> = PartChild<T, OptionalPartKind>;
#[cfg(feature = "parts")]
pub type RequiredPart<T> = PartChild<T, RequiredPartKind>;
#[cfg(feature = "parts")]
pub type RepeatedPart<T> = PartChild<T, RepeatedPartKind>;
#[cfg(feature = "parts")]
impl<T, C> PartChild<T, C> {
#[inline]
pub const fn new() -> Self {
Self(std::marker::PhantomData)
}
}
#[cfg(feature = "parts")]
impl<T, C> Clone for PartChild<T, C> {
fn clone(&self) -> Self {
*self
}
}
#[cfg(feature = "parts")]
impl<T, C> Copy for PartChild<T, C> {}
#[cfg(feature = "parts")]
impl<T, C> std::fmt::Debug for PartChild<T, C> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("PartChild").finish()
}
}
#[cfg(feature = "parts")]
impl<T, C> Default for PartChild<T, C> {
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "parts")]
impl<T, C> Eq for PartChild<T, C> {}
#[cfg(feature = "parts")]
impl<T, C> PartialEq for PartChild<T, C> {
fn eq(&self, _other: &Self) -> bool {
true
}
}
#[cfg(feature = "parts")]
pub struct PartRoot<T>(std::marker::PhantomData<T>);
#[cfg(feature = "parts")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum RelationshipModelEntry {
Child { field_index: u16, item_index: usize },
Fallback(usize),
Relationship(usize),
}
#[cfg(feature = "parts")]
impl<T> PartRoot<T> {
#[inline]
pub const fn new() -> Self {
Self(std::marker::PhantomData)
}
}
#[cfg(feature = "parts")]
impl<T> Clone for PartRoot<T> {
fn clone(&self) -> Self {
*self
}
}
#[cfg(feature = "parts")]
impl<T> Copy for PartRoot<T> {}
#[cfg(feature = "parts")]
impl<T> std::fmt::Debug for PartRoot<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("PartRoot").finish()
}
}
#[cfg(feature = "parts")]
impl<T> Default for PartRoot<T> {
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "parts")]
impl<T> Eq for PartRoot<T> {}
#[cfg(feature = "parts")]
impl<T> PartialEq for PartRoot<T> {
fn eq(&self, _other: &Self) -> bool {
true
}
}
#[cfg(feature = "parts")]
fn collect_all_parts_from_relationships<P: SdkPackage + Sized>(
package: &P,
relationships: &crate::common::RelationshipSet,
) -> Vec<crate::parts::PartRef> {
let mut parts = Vec::new();
let mut visited = std::collections::HashSet::new();
let mut queue = std::collections::VecDeque::new();
for relationship in relationships.part_relationships() {
if is_data_part_reference_relationship(relationship) {
continue;
}
if let Some(part_id) = relationship.target_part_id()
&& visited.insert(part_id)
{
queue.push_back(part_id);
}
}
while let Some(part_id) = queue.pop_front() {
if let Some(part) = crate::parts::PartRef::from_part_id(package, part_id) {
parts.push(part);
}
if let Some(relationships) = crate::sdk::SdkPackage::storage(package).relationships(part_id) {
for relationship in relationships.part_relationships() {
if is_data_part_reference_relationship(relationship) {
continue;
}
if let Some(child_part_id) = relationship.target_part_id()
&& visited.insert(child_part_id)
{
queue.push_back(child_part_id);
}
}
}
}
parts
}
#[cfg(feature = "parts")]
fn relationship_target_as_part<T: SdkPart>(
storage: &crate::common::SdkPackageStorage,
relationship: &crate::common::RelationshipInfo,
) -> Option<T> {
let part_id = relationship.target_part_id()?;
let part = storage.part(part_id)?;
if std::any::TypeId::of::<T>()
== std::any::TypeId::of::<crate::parts::extended_part::ExtendedPart>()
{
if let Some(crate::parts::PartRef::ExtendedPart(part)) =
crate::parts::PartRef::from_relationship_storage(storage, relationship)
{
let part: Box<dyn std::any::Any> = Box::new(part);
return part.downcast::<T>().ok().map(|part| *part);
}
return None;
}
let part_relationship_type = part.relationship_type_bytes()?;
if !crate::common::relationship_type_matches_bytes(
part_relationship_type,
T::RELATIONSHIP_TYPE.as_bytes(),
) {
return None;
}
if T::CONTENT_TYPE.is_empty()
&& T::RELATIONSHIP_TYPE.as_bytes() == crate::common::REL_OFFICE_DOCUMENT
{
return crate::common::package_main_part_path_matches(
part.path(),
T::PATH_PREFIX,
T::TARGET_NAME,
)
.then(|| T::from_relationship_id(relationship.id(), part_id));
}
if T::CONTENT_TYPE.is_empty() || part.content_type().as_bytes() == T::CONTENT_TYPE.as_bytes() {
return Some(T::from_relationship_id(relationship.id(), part_id));
}
(T::RELATIONSHIP_TYPE.as_bytes() == crate::common::REL_OFFICE_DOCUMENT
&& crate::common::package_main_part_path_matches(part.path(), T::PATH_PREFIX, T::TARGET_NAME))
.then(|| T::from_relationship_id(relationship.id(), part_id))
}
#[cfg(feature = "parts")]
#[inline]
fn is_data_part_reference_relationship(relationship: &crate::common::RelationshipInfo) -> bool {
crate::common::is_data_part_reference_relationship_type_bytes(
relationship.relationship_type_bytes(),
)
}
#[cfg(feature = "parts")]
pub fn default_main_part_content_type<T: SdkPart>() -> Option<&'static str> {
match (T::RELATIONSHIP_TYPE, T::TARGET_NAME) {
(
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
"document",
) => Some("application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"),
(
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
"workbook",
) => Some("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"),
(
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
"presentation",
) => Some("application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"),
_ => None,
}
}
#[cfg(feature = "parts")]
pub(crate) fn typed_main_part_content_type<T: SdkPart>(
storage: &crate::common::SdkPackageStorage,
) -> std::borrow::Cow<'static, str> {
if T::CONTENT_TYPE.is_empty() {
storage
.preferred_main_part_content_type()
.map(|content_type| std::borrow::Cow::Owned(content_type.to_string()))
.or_else(|| default_main_part_content_type::<T>().map(std::borrow::Cow::Borrowed))
.unwrap_or(std::borrow::Cow::Borrowed(T::CONTENT_TYPE))
} else {
std::borrow::Cow::Borrowed(T::CONTENT_TYPE)
}
}
#[cfg(feature = "parts")]
fn extension_for_content_type<T: SdkPart>(content_type: &str) -> std::borrow::Cow<'static, str> {
crate::common::default_part_extension_for_content_type(content_type)
.map(std::borrow::Cow::Borrowed)
.unwrap_or(std::borrow::Cow::Borrowed(T::EXTENSION))
}
#[cfg(feature = "parts")]
#[inline]
pub(crate) fn part_root_content_type_matches_bytes(
root_content_type: &[u8],
part_content_type: &[u8],
) -> bool {
if matches!(root_content_type, b"" | b"application/xml" | b"text/xml") {
return false;
}
root_content_type == part_content_type
|| match root_content_type {
b"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" => {
WordprocessingDocumentType::from_content_type_bytes(part_content_type).is_some()
}
b"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" => {
SpreadsheetDocumentType::from_content_type_bytes(part_content_type).is_some()
}
b"application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml" => {
PresentationDocumentType::from_content_type_bytes(part_content_type).is_some()
}
_ => false,
}
}
#[cfg(feature = "parts")]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum WordprocessingDocumentType {
#[default]
Document,
Template,
MacroEnabledDocument,
MacroEnabledTemplate,
}
#[cfg(feature = "parts")]
impl WordprocessingDocumentType {
pub const fn content_type(self) -> &'static str {
match self {
Self::Document => {
"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"
}
Self::Template => {
"application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml"
}
Self::MacroEnabledDocument => "application/vnd.ms-word.document.macroEnabled.main+xml",
Self::MacroEnabledTemplate => {
"application/vnd.ms-word.template.macroEnabledTemplate.main+xml"
}
}
}
pub fn from_content_type(content_type: &str) -> Option<Self> {
Self::from_content_type_bytes(content_type.as_bytes())
}
pub fn from_content_type_bytes(content_type: &[u8]) -> Option<Self> {
match content_type {
b"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" => {
Some(Self::Document)
}
b"application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml" => {
Some(Self::Template)
}
b"application/vnd.ms-word.document.macroEnabled.main+xml" => Some(Self::MacroEnabledDocument),
b"application/vnd.ms-word.template.macroEnabledTemplate.main+xml" => {
Some(Self::MacroEnabledTemplate)
}
_ => None,
}
}
}
#[cfg(feature = "parts")]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum SpreadsheetDocumentType {
#[default]
Workbook,
Template,
MacroEnabledWorkbook,
MacroEnabledTemplate,
AddIn,
}
#[cfg(feature = "parts")]
impl SpreadsheetDocumentType {
pub const fn content_type(self) -> &'static str {
match self {
Self::Workbook => {
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"
}
Self::Template => {
"application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml"
}
Self::MacroEnabledWorkbook => "application/vnd.ms-excel.sheet.macroEnabled.main+xml",
Self::MacroEnabledTemplate => "application/vnd.ms-excel.template.macroEnabled.main+xml",
Self::AddIn => "application/vnd.ms-excel.addin.macroEnabled.main+xml",
}
}
pub fn from_content_type(content_type: &str) -> Option<Self> {
Self::from_content_type_bytes(content_type.as_bytes())
}
pub fn from_content_type_bytes(content_type: &[u8]) -> Option<Self> {
match content_type {
b"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" => {
Some(Self::Workbook)
}
b"application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml" => {
Some(Self::Template)
}
b"application/vnd.ms-excel.sheet.macroEnabled.main+xml" => Some(Self::MacroEnabledWorkbook),
b"application/vnd.ms-excel.template.macroEnabled.main+xml" => {
Some(Self::MacroEnabledTemplate)
}
b"application/vnd.ms-excel.addin.macroEnabled.main+xml" => Some(Self::AddIn),
_ => None,
}
}
}
#[cfg(feature = "parts")]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum PresentationDocumentType {
#[default]
Presentation,
Template,
Slideshow,
MacroEnabledPresentation,
MacroEnabledTemplate,
MacroEnabledSlideshow,
AddIn,
}
#[cfg(feature = "parts")]
impl PresentationDocumentType {
pub const fn content_type(self) -> &'static str {
match self {
Self::Presentation => {
"application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"
}
Self::Template => {
"application/vnd.openxmlformats-officedocument.presentationml.template.main+xml"
}
Self::Slideshow => {
"application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml"
}
Self::MacroEnabledPresentation => {
"application/vnd.ms-powerpoint.presentation.macroEnabled.main+xml"
}
Self::MacroEnabledTemplate => "application/vnd.ms-powerpoint.template.macroEnabled.main+xml",
Self::MacroEnabledSlideshow => {
"application/vnd.ms-powerpoint.slideshow.macroEnabled.main+xml"
}
Self::AddIn => "application/vnd.ms-powerpoint.addin.macroEnabled.main+xml",
}
}
pub fn from_content_type(content_type: &str) -> Option<Self> {
Self::from_content_type_bytes(content_type.as_bytes())
}
pub fn from_content_type_bytes(content_type: &[u8]) -> Option<Self> {
match content_type {
b"application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml" => {
Some(Self::Presentation)
}
b"application/vnd.openxmlformats-officedocument.presentationml.template.main+xml" => {
Some(Self::Template)
}
b"application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml" => {
Some(Self::Slideshow)
}
b"application/vnd.ms-powerpoint.presentation.macroEnabled.main+xml" => {
Some(Self::MacroEnabledPresentation)
}
b"application/vnd.ms-powerpoint.template.macroEnabled.main+xml" => {
Some(Self::MacroEnabledTemplate)
}
b"application/vnd.ms-powerpoint.slideshow.macroEnabled.main+xml" => {
Some(Self::MacroEnabledSlideshow)
}
b"application/vnd.ms-powerpoint.addin.macroEnabled.main+xml" => Some(Self::AddIn),
_ => None,
}
}
}
pub trait SdkEnum: Sized {
fn as_xml_str(&self) -> &str;
fn from_xml_bytes(value: &[u8]) -> Result<Self, crate::common::SdkError>;
fn to_xml(&self) -> String {
self.as_xml_str().to_string()
}
}
pub trait SdkType: Sized {
fn read_borrowed_inner<'de>(
_xml_reader: &mut crate::common::SliceReader<'de>,
_start: quick_xml::events::BytesStart<'de>,
_empty: bool,
) -> Result<Self, crate::common::SdkError> {
Err(crate::common::SdkError::CommonError(
"SdkType does not support borrowed deserialization".to_string(),
))
}
fn read_io_inner<R: std::io::BufRead>(
_xml_reader: &mut crate::common::IoReader<R>,
_start: quick_xml::events::BytesStart<'static>,
_empty: bool,
) -> Result<Self, crate::common::SdkError> {
Err(crate::common::SdkError::CommonError(
"SdkType does not support IO deserialization".to_string(),
))
}
}
#[cfg(feature = "mce")]
pub trait SdkMce {
fn process_mce(
&mut self,
settings: &MarkupCompatibilityProcessSettings,
) -> Result<(), crate::common::SdkError> {
let mut context = MceContext::default();
self.process_mce_with_context(settings, &mut context)
}
fn process_mce_with_context(
&mut self,
_settings: &MarkupCompatibilityProcessSettings,
_context: &mut MceContext,
) -> Result<(), crate::common::SdkError> {
Ok(())
}
}
#[cfg(feature = "mce")]
impl<T: SdkMce + ?Sized> SdkMce for Box<T> {
#[inline]
fn process_mce(
&mut self,
settings: &MarkupCompatibilityProcessSettings,
) -> Result<(), crate::common::SdkError> {
self.as_mut().process_mce(settings)
}
#[inline]
fn process_mce_with_context(
&mut self,
settings: &MarkupCompatibilityProcessSettings,
context: &mut MceContext,
) -> Result<(), crate::common::SdkError> {
self.as_mut().process_mce_with_context(settings, context)
}
}
#[cfg(feature = "mce")]
#[derive(Clone, Debug, Default)]
pub struct MceContext {
namespaces: Vec<crate::common::XmlNamespace>,
ignorable_namespaces: Vec<Box<[u8]>>,
process_content: Vec<Box<[u8]>>,
preserve_attributes: Vec<Box<[u8]>>,
}
#[cfg(feature = "mce")]
#[derive(Clone, Copy, Debug)]
pub struct MceContextCheckpoint {
namespaces: usize,
ignorable_namespaces: usize,
process_content: usize,
preserve_attributes: usize,
}
#[cfg(feature = "mce")]
impl MceContext {
pub(crate) fn push(
&mut self,
namespaces: &[crate::common::XmlNamespace],
attrs: &[crate::common::XmlOtherAttr],
settings: &MarkupCompatibilityProcessSettings,
) -> Result<MceContextCheckpoint, crate::common::SdkError> {
let checkpoint = MceContextCheckpoint {
namespaces: self.namespaces.len(),
ignorable_namespaces: self.ignorable_namespaces.len(),
process_content: self.process_content.len(),
preserve_attributes: self.preserve_attributes.len(),
};
self.namespaces.extend_from_slice(namespaces);
if let Some(value) = mce_attr(attrs, b"Ignorable") {
for prefix in split_ascii_whitespace(value) {
if let Some(ns) = self.namespace_for_prefix_bytes(prefix) {
self.ignorable_namespaces.push(ns.into());
}
}
}
if let Some(value) = mce_attr(attrs, b"ProcessContent") {
self
.process_content
.extend(split_ascii_whitespace(value).map(Into::into));
}
if let Some(value) = mce_attr(attrs, b"PreserveAttributes") {
self
.preserve_attributes
.extend(split_ascii_whitespace(value).map(Into::into));
}
if let Some(value) = mce_attr(attrs, b"MustUnderstand") {
for prefix in split_ascii_whitespace(value) {
let Some(ns) = self.namespace_for_prefix_bytes(prefix) else {
let prefix = String::from_utf8_lossy(prefix);
return Err(crate::common::SdkError::CommonError(format!(
"MCE MustUnderstand prefix `{prefix}` is not declared"
)));
};
if !namespace_supported(ns, settings.target_file_format_version) {
let namespace = String::from_utf8_lossy(ns);
return Err(crate::common::SdkError::CommonError(format!(
"MCE MustUnderstand namespace `{namespace}` is not supported by target file format {:?}",
settings.target_file_format_version
)));
}
}
}
Ok(checkpoint)
}
pub(crate) fn pop(&mut self, checkpoint: MceContextCheckpoint) {
self.namespaces.truncate(checkpoint.namespaces);
self
.ignorable_namespaces
.truncate(checkpoint.ignorable_namespaces);
self.process_content.truncate(checkpoint.process_content);
self
.preserve_attributes
.truncate(checkpoint.preserve_attributes);
}
pub(crate) fn is_process_content_qname_bytes(&self, qname: &[u8]) -> bool {
self.process_content.iter().any(|candidate| {
let candidate = candidate.as_ref();
candidate == b"*"
|| candidate == qname
|| candidate
.strip_suffix(b":*")
.zip(qname.iter().position(|byte| *byte == b':'))
.is_some_and(|(prefix, qname_prefix_len)| prefix == &qname[..qname_prefix_len])
})
}
pub(crate) fn should_remove_ignorable_attribute_bytes(&self, qname: &[u8]) -> bool {
let Some(prefix_len) = qname.iter().position(|byte| *byte == b':') else {
return false;
};
let Some(namespace) = self.namespace_for_prefix_bytes(&qname[..prefix_len]) else {
return false;
};
self.is_ignorable_namespace_bytes(namespace) && !self.is_preserved_attribute_qname_bytes(qname)
}
fn is_preserved_attribute_qname_bytes(&self, qname: &[u8]) -> bool {
self.preserve_attributes.iter().any(|candidate| {
let candidate = candidate.as_ref();
candidate == b"*"
|| candidate == qname
|| candidate
.strip_suffix(b":*")
.zip(qname.iter().position(|byte| *byte == b':'))
.is_some_and(|(prefix, qname_prefix_len)| prefix == &qname[..qname_prefix_len])
})
}
pub(crate) fn namespace_for_prefix_bytes(&self, prefix: &[u8]) -> Option<&[u8]> {
self.namespaces.iter().rev().find_map(|candidate| {
let (candidate_prefix, candidate_uri) = candidate.parts();
(candidate_prefix == prefix).then_some(candidate_uri)
})
}
pub(crate) fn is_ignorable_namespace_bytes(&self, namespace: &[u8]) -> bool {
self
.ignorable_namespaces
.iter()
.any(|candidate| candidate.as_ref() == namespace)
}
pub(crate) fn namespaces(&self) -> &[crate::common::XmlNamespace] {
self.namespaces.as_slice()
}
}
#[cfg(feature = "mce")]
pub(crate) fn namespace_supported(ns: &[u8], target: FileFormatVersion) -> bool {
namespace_minimum_version(ns)
.is_some_and(|version| file_format_rank(version) <= file_format_rank(target))
}
#[cfg(feature = "mce")]
fn namespace_minimum_version(ns: &[u8]) -> Option<FileFormatVersion> {
if bytes_contains(ns, b"openxmlformats.org")
|| bytes_contains(ns, b"/2006/")
|| bytes_contains(ns, b":office:")
{
Some(FileFormatVersion::Office2007)
} else if bytes_contains(ns, b"/2010/") || bytes_contains(ns, b"14") {
Some(FileFormatVersion::Office2010)
} else if bytes_contains(ns, b"/2012/") || bytes_contains(ns, b"15") {
Some(FileFormatVersion::Office2013)
} else if bytes_contains(ns, b"/2016/") || bytes_contains(ns, b"16") {
Some(FileFormatVersion::Office2016)
} else if bytes_contains(ns, b"/2019/") {
Some(FileFormatVersion::Office2019)
} else if bytes_contains(ns, b"/2021/") {
Some(FileFormatVersion::Office2021)
} else if bytes_contains(ns, b"/2022/")
|| bytes_contains(ns, b"/2023/")
|| bytes_contains(ns, b"/2024/")
|| bytes_contains(ns, b"/2025/")
{
Some(FileFormatVersion::Microsoft365)
} else {
None
}
}
#[cfg(feature = "mce")]
fn bytes_contains(haystack: &[u8], needle: &[u8]) -> bool {
haystack
.windows(needle.len())
.any(|window| window == needle)
}
#[cfg(feature = "mce")]
fn file_format_rank(version: FileFormatVersion) -> u8 {
match version {
FileFormatVersion::Office2007 => 0,
FileFormatVersion::Office2010 => 1,
FileFormatVersion::Office2013 => 2,
FileFormatVersion::Office2016 => 3,
FileFormatVersion::Office2019 => 4,
FileFormatVersion::Office2021 => 5,
FileFormatVersion::Microsoft365 => 6,
}
}
#[cfg(feature = "mce")]
fn mce_attr<'a>(attrs: &'a [crate::common::XmlOtherAttr], local_name: &[u8]) -> Option<&'a [u8]> {
attrs.iter().find_map(|attr| {
let name = attr.name_bytes();
(name.strip_prefix(b"mc:") == Some(local_name) || name == local_name)
.then_some(attr.raw_value_bytes())
})
}
#[cfg(feature = "mce")]
fn split_ascii_whitespace(value: &[u8]) -> impl Iterator<Item = &[u8]> {
value
.split(u8::is_ascii_whitespace)
.filter(|part| !part.is_empty())
}
#[cfg(feature = "parts")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum AlternativeFormatImportPartType {
Html,
Xhtml,
Xml,
WordprocessingMl,
}
#[cfg(feature = "parts")]
impl AlternativeFormatImportPartType {
#[inline]
pub const fn content_type(self) -> &'static str {
match self {
Self::Html => "text/html",
Self::Xhtml => "application/xhtml+xml",
Self::Xml => "application/xml",
Self::WordprocessingMl => {
"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"
}
}
}
}
#[cfg(feature = "parts")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CustomXmlPartType {
AdditionalCharacteristicsInfo,
Bibliography,
CustomXml,
InkContent,
}
#[cfg(feature = "parts")]
impl CustomXmlPartType {
#[inline]
pub const fn content_type(self) -> &'static str {
match self {
Self::AdditionalCharacteristicsInfo | Self::Bibliography | Self::CustomXml => {
"application/xml"
}
Self::InkContent => "application/inkml+xml",
}
}
}
#[cfg(feature = "parts")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CustomPropertyPartType {
Spreadsheet,
Xml,
}
#[cfg(feature = "parts")]
impl CustomPropertyPartType {
#[inline]
pub const fn content_type(self) -> &'static str {
match self {
Self::Spreadsheet => {
"application/vnd.openxmlformats-officedocument.spreadsheetml.customProperty"
}
Self::Xml => "application/xml",
}
}
#[inline]
pub const fn extension(self) -> &'static str {
".xml"
}
}
#[cfg(feature = "parts")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum EmbeddedObjectPartType {
Binary,
}
#[cfg(feature = "parts")]
impl EmbeddedObjectPartType {
#[inline]
pub const fn content_type(self) -> &'static str {
match self {
Self::Binary => "application/vnd.openxmlformats-officedocument.oleObject",
}
}
#[inline]
pub const fn extension(self) -> &'static str {
".bin"
}
}
#[cfg(feature = "parts")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum EmbeddedPackagePartType {
Docm,
Docx,
Dotm,
Dotx,
Potm,
Potx,
Ppam,
Ppsm,
Ppsx,
Pptm,
Pptx,
Sldm,
Sldx,
Thmx,
Xlam,
Xlsb,
Xlsm,
Xlsx,
Xltm,
Xltx,
}
#[cfg(feature = "parts")]
impl EmbeddedPackagePartType {
#[inline]
pub const fn content_type(self) -> &'static str {
match self {
Self::Docm => "application/vnd.ms-word.document.macroEnabled.12",
Self::Docx => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
Self::Dotm => "application/vnd.ms-word.template.macroEnabled.12",
Self::Dotx => "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
Self::Potm => "application/vnd.ms-powerpoint.template.macroEnabled.12",
Self::Potx => "application/vnd.openxmlformats-officedocument.presentationml.template",
Self::Ppam => "application/vnd.ms-powerpoint.addin.macroEnabled.12",
Self::Ppsm => "application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
Self::Ppsx => "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
Self::Pptm => "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
Self::Pptx => "application/vnd.openxmlformats-officedocument.presentationml.presentation",
Self::Sldm => "application/vnd.ms-powerpoint.slide.macroEnabled.12",
Self::Sldx => "application/vnd.openxmlformats-officedocument.presentationml.slide",
Self::Thmx => "application/vnd.ms-officetheme",
Self::Xlam => "application/vnd.ms-excel.addin.macroEnabled.12",
Self::Xlsb => "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
Self::Xlsm => "application/vnd.ms-excel.sheet.macroEnabled.12",
Self::Xlsx => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
Self::Xltm => "application/vnd.ms-excel.template.macroEnabled.12",
Self::Xltx => "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
}
}
#[inline]
pub const fn extension(self) -> &'static str {
match self {
Self::Docm => ".docm",
Self::Docx => ".docx",
Self::Dotm => ".dotm",
Self::Dotx => ".dotx",
Self::Potm => ".potm",
Self::Potx => ".potx",
Self::Ppam => ".ppam",
Self::Ppsm => ".ppsm",
Self::Ppsx => ".ppsx",
Self::Pptm => ".pptm",
Self::Pptx => ".pptx",
Self::Sldm => ".sldm",
Self::Sldx => ".sldx",
Self::Thmx => ".thmx",
Self::Xlam => ".xlam",
Self::Xlsb => ".xlsb",
Self::Xlsm => ".xlsm",
Self::Xlsx => ".xlsx",
Self::Xltm => ".xltm",
Self::Xltx => ".xltx",
}
}
}
#[cfg(feature = "parts")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FontPartType {
FontData,
FontTtf,
FontOdttf,
}
#[cfg(feature = "parts")]
impl FontPartType {
#[inline]
pub const fn content_type(self) -> &'static str {
match self {
Self::FontData => "application/x-fontdata",
Self::FontTtf => "application/x-font-ttf",
Self::FontOdttf => "application/vnd.openxmlformats-officedocument.obfuscatedFont",
}
}
#[inline]
pub const fn extension(self) -> &'static str {
match self {
Self::FontData => ".fntdata",
Self::FontTtf => ".ttf",
Self::FontOdttf => ".odttf",
}
}
}
#[cfg(feature = "parts")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MailMergeRecipientDataPartType {
OpenXmlMailMergeRecipientData,
MsWordMailMergeRecipientData,
}
#[cfg(feature = "parts")]
impl MailMergeRecipientDataPartType {
#[inline]
pub const fn content_type(self) -> &'static str {
match self {
Self::OpenXmlMailMergeRecipientData => {
"application/vnd.openxmlformats-officedocument.wordprocessingml.mailMergeRecipientData+xml"
}
Self::MsWordMailMergeRecipientData => "application/vnd.ms-word.mailMergeRecipientData+xml",
}
}
#[inline]
pub const fn extension(self) -> &'static str {
".xml"
}
}
#[cfg(feature = "parts")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum EmbeddedControlPersistenceBinaryDataPartType {
ActiveXBin,
}
#[cfg(feature = "parts")]
impl EmbeddedControlPersistenceBinaryDataPartType {
#[inline]
pub const fn content_type(self) -> &'static str {
match self {
Self::ActiveXBin => "application/vnd.ms-office.activeX",
}
}
#[inline]
pub const fn extension(self) -> &'static str {
".bin"
}
}
#[cfg(feature = "parts")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum EmbeddedControlPersistencePartType {
ActiveX,
ActiveXBin,
}
#[cfg(feature = "parts")]
impl EmbeddedControlPersistencePartType {
#[inline]
pub const fn content_type(self) -> &'static str {
match self {
Self::ActiveX => "application/vnd.ms-office.activeX+xml",
Self::ActiveXBin => "application/vnd.ms-office.activeX",
}
}
#[inline]
pub const fn extension(self) -> &'static str {
match self {
Self::ActiveX => ".xml",
Self::ActiveXBin => ".bin",
}
}
}
#[cfg(feature = "parts")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ThumbnailPartType {
Jpeg,
Emf,
Wmf,
}
#[cfg(feature = "parts")]
impl ThumbnailPartType {
#[inline]
pub const fn content_type(self) -> &'static str {
match self {
Self::Jpeg => "image/jpeg",
Self::Emf => "image/x-emf",
Self::Wmf => "image/x-wmf",
}
}
#[inline]
pub const fn extension(self) -> &'static str {
match self {
Self::Jpeg => ".jpg",
Self::Emf => ".emf",
Self::Wmf => ".wmf",
}
}
}
#[cfg(feature = "parts")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MediaDataPartType {
Aiff,
Midi,
Mp3,
MpegUrl,
Wav,
Wma,
MpegAudio,
OggAudio,
Asx,
Avi,
Mpg,
MpegVideo,
Wmv,
Wmx,
Wvx,
Quicktime,
OggVideo,
Vc1,
Mp4,
}
#[cfg(feature = "parts")]
impl MediaDataPartType {
#[inline]
pub const fn content_type(self) -> &'static str {
match self {
Self::Aiff => "audio/aiff",
Self::Midi => "audio/midi",
Self::Mp3 => "audio/mp3",
Self::MpegUrl => "audio/mpegurl",
Self::Wav => "audio/wav",
Self::Wma => "audio/x-ms-wma",
Self::MpegAudio => "audio/mpeg",
Self::OggAudio => "audio/ogg",
Self::Asx => "video/x-ms-asf-plugin",
Self::Avi => "video/avi",
Self::Mpg => "video/mpg",
Self::MpegVideo => "video/mpeg",
Self::Wmv => "video/x-ms-wmv",
Self::Wmx => "video/x-ms-wmx",
Self::Wvx => "video/x-ms-wvx",
Self::Quicktime => "video/quicktime",
Self::OggVideo => "video/ogg",
Self::Vc1 => "video/vc1",
Self::Mp4 => "video/mp4",
}
}
#[inline]
pub const fn extension(self) -> &'static str {
match self {
Self::Aiff => ".aiff",
Self::Midi => ".midi",
Self::Mp3 => ".mp3",
Self::MpegUrl => ".m3u",
Self::Wav => ".wav",
Self::Wma => ".wma",
Self::MpegAudio => ".mpeg",
Self::OggAudio => ".ogg",
Self::Asx => ".asx",
Self::Avi => ".avi",
Self::Mpg => ".mpg",
Self::MpegVideo => ".mpeg",
Self::Wmv => ".wmv",
Self::Wmx => ".wmx",
Self::Wvx => ".wvx",
Self::Quicktime => ".mov",
Self::OggVideo => ".ogg",
Self::Vc1 => ".wmv",
Self::Mp4 => ".mp4",
}
}
}
#[cfg(feature = "parts")]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum PackageOpenMode {
#[default]
Eager,
Lazy,
}
#[cfg(feature = "parts")]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum FileFormatVersion {
Office2007,
Office2010,
Office2013,
Office2016,
Office2019,
Office2021,
#[default]
Microsoft365,
}
#[cfg(feature = "parts")]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum MarkupCompatibilityProcessMode {
#[default]
NoProcess,
#[cfg(feature = "mce")]
ProcessLoadedPartsOnly,
#[cfg(feature = "mce")]
ProcessAllParts,
}
#[cfg(feature = "parts")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct MarkupCompatibilityProcessSettings {
pub process_mode: MarkupCompatibilityProcessMode,
pub target_file_format_version: FileFormatVersion,
}
#[cfg(feature = "parts")]
impl Default for MarkupCompatibilityProcessSettings {
#[inline]
fn default() -> Self {
Self {
process_mode: MarkupCompatibilityProcessMode::NoProcess,
target_file_format_version: FileFormatVersion::Office2007,
}
}
}
#[cfg(feature = "parts")]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct OpenSettings {
pub open_mode: PackageOpenMode,
pub markup_compatibility_process_settings: MarkupCompatibilityProcessSettings,
pub ignore_calculation_chain_part_relationship: bool,
}
#[cfg(feature = "parts")]
impl OpenSettings {
#[inline]
pub(crate) fn root_element_open_mode(self) -> PackageOpenMode {
#[cfg(feature = "mce")]
if matches!(
self.markup_compatibility_process_settings.process_mode,
MarkupCompatibilityProcessMode::ProcessAllParts
) {
return PackageOpenMode::Eager;
}
self.open_mode
}
}
#[cfg(feature = "parts")]
pub trait SdkPackage: Clone + Sized + 'static {
#[doc(hidden)]
fn storage(&self) -> &crate::common::SdkPackageStorage;
#[doc(hidden)]
fn storage_mut(&mut self) -> &mut crate::common::SdkPackageStorage;
#[inline]
fn open_settings(&self) -> &OpenSettings {
static DEFAULT_SETTINGS: OpenSettings = OpenSettings {
open_mode: PackageOpenMode::Eager,
markup_compatibility_process_settings: MarkupCompatibilityProcessSettings {
process_mode: MarkupCompatibilityProcessMode::NoProcess,
target_file_format_version: FileFormatVersion::Office2007,
},
ignore_calculation_chain_part_relationship: false,
};
&DEFAULT_SETTINGS
}
#[inline]
#[doc(hidden)]
fn relationships(&self) -> &crate::common::RelationshipSet {
crate::sdk::SdkPackage::storage(self).package_relationships()
}
#[inline]
#[doc(hidden)]
fn relationships_mut(&mut self) -> &mut crate::common::RelationshipSet {
crate::sdk::SdkPackage::storage_mut(self).package_relationships_mut()
}
#[doc(hidden)]
fn refresh_relationship_model_from_storage(&mut self);
#[doc(hidden)]
fn root_element(&self, part_id: crate::common::PartId) -> Option<&crate::parts::PartRootElement>;
#[doc(hidden)]
fn root_element_slot_mut(
&mut self,
part_id: crate::common::PartId,
) -> Option<&mut Option<crate::parts::PartRootElement>>;
#[doc(hidden)]
fn push_root_element_slot(&mut self);
#[inline]
#[doc(hidden)]
fn is_root_element_loaded(&self, part_id: crate::common::PartId) -> bool {
self.root_element(part_id).is_some()
}
#[inline]
#[doc(hidden)]
fn unload_root_element(
&mut self,
part_id: crate::common::PartId,
) -> Option<crate::parts::PartRootElement> {
self.root_element_slot_mut(part_id)?.take()
}
#[inline]
#[doc(hidden)]
fn part_bytes_for_copy(
&self,
part_id: crate::common::PartId,
) -> Result<Vec<u8>, crate::common::SdkError> {
if let Some(root_element) = self.root_element(part_id) {
root_element.to_bytes()
} else {
let part = Self::storage(self).part(part_id).ok_or_else(|| {
crate::common::SdkError::CommonError(format!(
"part id {part_id:?} is not present in package storage"
))
})?;
Ok(part.data().bytes().to_vec())
}
}
#[inline]
fn load_all_parts(&mut self) -> Result<(), crate::common::SdkError>
where
Self: Sized,
{
crate::parts::load_all_part_roots(self)
}
#[cfg(feature = "flat-opc")]
#[inline]
fn write_flat_opc_to<W: std::io::Write>(
&self,
writer: &mut W,
) -> Result<(), crate::common::SdkError> {
crate::sdk::SdkPackage::storage(self).write_flat_opc(writer, |part_id, part| {
if let Some(root_element) = crate::sdk::SdkPackage::root_element(self, part_id) {
root_element.to_bytes()
} else {
Ok(part.data().bytes().to_vec())
}
})
}
#[inline]
fn add_external_relationship(
&mut self,
relationship_id: impl Into<String>,
relationship_type: impl Into<String>,
target: impl Into<String>,
) -> Result<crate::common::RelationshipRef<'_>, crate::common::SdkError> {
let relationship_id = relationship_id.into();
crate::sdk::SdkPackage::relationships_mut(self).add_external_relationship(
relationship_id.clone(),
relationship_type,
target,
)?;
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(self);
Ok(
crate::sdk::SdkPackage::relationships(self)
.get(&relationship_id)
.expect("relationship was just added")
.into(),
)
}
#[inline]
fn add_external_relationship_auto_id(
&mut self,
relationship_type: impl Into<String>,
target: impl Into<String>,
) -> Result<crate::common::RelationshipRef<'_>, crate::common::SdkError> {
let relationship_id = crate::sdk::SdkPackage::relationships(self).next_relationship_id();
self.add_external_relationship(relationship_id, relationship_type, target)
}
#[inline]
fn add_hyperlink_relationship(
&mut self,
relationship_id: impl Into<String>,
target: impl Into<String>,
) -> Result<crate::common::RelationshipRef<'_>, crate::common::SdkError> {
let relationship_id = relationship_id.into();
crate::sdk::SdkPackage::relationships_mut(self)
.add_hyperlink_relationship(relationship_id.clone(), target)?;
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(self);
Ok(
crate::sdk::SdkPackage::relationships(self)
.get(&relationship_id)
.expect("relationship was just added")
.into(),
)
}
#[inline]
fn add_hyperlink_relationship_with_mode(
&mut self,
relationship_id: impl Into<String>,
target: impl Into<String>,
target_mode: crate::schemas::opc_relationships::TargetMode,
) -> Result<crate::common::RelationshipRef<'_>, crate::common::SdkError> {
let relationship_id = relationship_id.into();
crate::sdk::SdkPackage::relationships_mut(self).add_hyperlink_relationship_with_mode(
relationship_id.clone(),
target,
target_mode,
)?;
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(self);
Ok(
crate::sdk::SdkPackage::relationships(self)
.get(&relationship_id)
.expect("relationship was just added")
.into(),
)
}
#[inline]
fn add_hyperlink_relationship_auto_id(
&mut self,
target: impl Into<String>,
target_mode: crate::schemas::opc_relationships::TargetMode,
) -> Result<crate::common::RelationshipRef<'_>, crate::common::SdkError> {
let relationship_id = crate::sdk::SdkPackage::relationships(self).next_relationship_id();
self.add_hyperlink_relationship_with_mode(relationship_id, target, target_mode)
}
#[inline]
fn get_reference_relationship(
&self,
relationship_id: &str,
) -> Option<crate::common::RelationshipRef<'_>> {
crate::sdk::SdkPackage::relationships(self)
.get(relationship_id)
.filter(|relationship| relationship.is_reference_relationship())
.map(Into::into)
}
#[inline]
fn get_external_relationship(
&self,
relationship_id: &str,
) -> Option<crate::common::RelationshipRef<'_>> {
crate::sdk::SdkPackage::relationships(self)
.get_external_relationship(relationship_id)
.map(Into::into)
}
#[inline]
fn get_hyperlink_relationship(
&self,
relationship_id: &str,
) -> Option<crate::common::RelationshipRef<'_>> {
crate::sdk::SdkPackage::relationships(self)
.get_hyperlink_relationship(relationship_id)
.map(Into::into)
}
#[inline]
fn delete_reference_relationship(
&mut self,
relationship_id: &str,
) -> Result<crate::common::Relationship, crate::common::SdkError> {
let relationship = crate::sdk::SdkPackage::relationships_mut(self)
.remove_reference_relationship(relationship_id)?;
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(self);
Ok(relationship.into())
}
#[inline]
fn delete_external_relationship(
&mut self,
relationship_id: &str,
) -> Result<crate::common::Relationship, crate::common::SdkError> {
let relationship = crate::sdk::SdkPackage::relationships_mut(self)
.remove_external_relationship(relationship_id)?;
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(self);
Ok(relationship.into())
}
#[inline]
fn change_relationship_id(
&mut self,
relationship_id: &str,
new_relationship_id: impl Into<String>,
) -> Result<(), crate::common::SdkError> {
crate::sdk::SdkPackage::relationships_mut(self)
.change_relationship_id(relationship_id, new_relationship_id)?;
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(self);
Ok(())
}
#[inline]
fn external_relationships(&self) -> impl Iterator<Item = crate::common::RelationshipRef<'_>> {
crate::sdk::SdkPackage::relationships(self)
.external_relationships()
.map(Into::into)
}
#[inline]
fn hyperlink_relationships(&self) -> impl Iterator<Item = crate::common::RelationshipRef<'_>> {
crate::sdk::SdkPackage::relationships(self)
.hyperlink_relationships()
.map(Into::into)
}
#[inline]
fn data_part_reference_relationships(
&self,
) -> impl Iterator<Item = crate::common::RelationshipRef<'_>> {
crate::sdk::SdkPackage::relationships(self)
.data_part_reference_relationships()
.map(Into::into)
}
#[inline]
fn media_data_parts(&self) -> impl Iterator<Item = crate::common::MediaDataPart> + '_ {
crate::sdk::SdkPackage::storage(self)
.media_data_parts()
.map(|(part_id, part)| {
crate::common::MediaDataPart::from_part_id(
crate::sdk::SdkPackage::storage(self).id(),
part_id,
part.path(),
)
})
}
#[inline]
fn delete_unused_media_data_parts(&mut self) -> usize {
crate::sdk::SdkPackage::storage_mut(self).delete_unused_media_data_parts()
}
#[inline]
fn parts(&self) -> impl Iterator<Item = crate::parts::IdPartPair<'_>> + '_
where
Self: Sized,
{
crate::sdk::SdkPackage::relationships(self)
.iter()
.filter_map(|relationship| {
let part_id = relationship.target_part_id()?;
let part = crate::parts::PartRef::from_part_id(self, part_id)?;
Some(crate::parts::IdPartPair::new(relationship.id(), part))
})
}
#[inline]
fn get_all_parts(&self) -> impl Iterator<Item = crate::parts::PartRef> + '_
where
Self: Sized,
{
collect_all_parts_from_relationships(self, crate::sdk::SdkPackage::relationships(self))
.into_iter()
}
#[inline]
fn get_part_by_id(&self, relationship_id: &str) -> Option<crate::parts::PartRef>
where
Self: Sized,
{
let part_id = crate::sdk::SdkPackage::relationships(self)
.get(relationship_id)?
.target_part_id()?;
crate::parts::PartRef::from_part_id(self, part_id)
}
#[inline]
fn get_part_by_id_required(
&self,
relationship_id: &str,
) -> Result<crate::parts::PartRef, crate::common::SdkError>
where
Self: Sized,
{
self.get_part_by_id(relationship_id).ok_or_else(|| {
crate::common::SdkError::CommonError(format!(
"part relationship id {relationship_id} does not exist"
))
})
}
#[inline]
fn try_get_part_by_id(&self, relationship_id: &str) -> Option<crate::parts::PartRef>
where
Self: Sized,
{
self.get_part_by_id(relationship_id)
}
#[inline]
fn get_parts_of_type<T: SdkPart>(&self) -> impl Iterator<Item = T> + '_
where
Self: Sized,
{
let storage = crate::sdk::SdkPackage::storage(self);
crate::sdk::SdkPackage::relationships(self)
.part_relationships()
.filter_map(move |relationship| relationship_target_as_part::<T>(storage, relationship))
}
#[inline]
fn related_parts_of_type<T: SdkPart>(&self) -> impl Iterator<Item = RelatedPart<'_, T>> + '_
where
Self: Sized,
{
let storage = crate::sdk::SdkPackage::storage(self);
crate::sdk::SdkPackage::relationships(self)
.part_relationships()
.filter_map(move |relationship| {
relationship_target_as_part::<T>(storage, relationship)
.map(|part| RelatedPart::new(relationship.id(), relationship.relationship_type(), part))
})
}
#[inline]
fn get_id_of_part<T: SdkPart>(&self, part: &T) -> Option<&str> {
let target_part_id = part.part_id();
crate::sdk::SdkPackage::relationships(self)
.iter()
.find_map(|relationship| {
(relationship.target_part_id() == Some(target_part_id)).then_some(relationship.id())
})
}
#[inline]
fn get_id_of_part_required<T: SdkPart>(&self, part: &T) -> Result<&str, crate::common::SdkError> {
self.get_id_of_part(part).ok_or_else(|| {
crate::common::SdkError::CommonError(format!(
"part id {:?} is not referenced by this package",
part.part_id()
))
})
}
#[inline]
fn change_id_of_part<T: SdkPart>(
&mut self,
part: &T,
new_relationship_id: impl Into<String>,
) -> Result<String, crate::common::SdkError> {
let old_relationship_id = self.get_id_of_part_required(part)?.to_string();
self.change_relationship_id(&old_relationship_id, new_relationship_id)?;
Ok(old_relationship_id)
}
#[inline]
fn delete_part_by_id(&mut self, relationship_id: &str) -> Result<bool, crate::common::SdkError> {
let deleted = crate::sdk::SdkPackage::storage_mut(self).delete_package_part(relationship_id)?;
if deleted {
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(self);
}
Ok(deleted)
}
#[inline]
fn delete_part<T: SdkPart>(&mut self, part: T) -> Result<bool, crate::common::SdkError> {
let Some(relationship_id) = self.get_id_of_part(&part).map(str::to_string) else {
return Ok(false);
};
self.delete_part_by_id(&relationship_id)
}
#[inline]
fn delete_parts<T, I>(&mut self, parts: I) -> Result<(), crate::common::SdkError>
where
T: SdkPart,
I: IntoIterator<Item = T>,
{
let relationship_ids: Vec<_> = parts
.into_iter()
.filter_map(|part| self.get_id_of_part(&part).map(str::to_string))
.collect();
for relationship_id in relationship_ids {
self.delete_part_by_id(&relationship_id)?;
}
Ok(())
}
#[inline]
fn add_part<T: SdkPart>(&mut self, part: T) -> Result<T, crate::common::SdkError> {
if self.get_id_of_part(&part).is_some() {
return Ok(part);
}
let relationship_id = crate::sdk::SdkPackage::relationships(self).next_relationship_id();
self.add_part_with_id(part, relationship_id)
}
#[inline]
fn add_part_with_id<T: SdkPart>(
&mut self,
part: T,
relationship_id: impl Into<String>,
) -> Result<T, crate::common::SdkError> {
let relationship_id = relationship_id.into();
let part_id = part.part_id();
crate::sdk::SdkPackage::storage_mut(self)
.add_package_relationship_to_part(relationship_id.clone(), part_id)?;
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(self);
Ok(T::from_relationship_id(relationship_id, part_id))
}
#[inline]
fn add_part_from_package<P, T>(
&mut self,
source_package: &P,
part: &T,
) -> Result<T, crate::common::SdkError>
where
P: SdkPackage,
T: SdkPart,
{
let relationship_id = part
.relationship_id()
.map(str::to_string)
.unwrap_or_else(|| crate::sdk::SdkPackage::relationships(self).next_relationship_id());
self.add_part_from_package_with_id(source_package, part, relationship_id)
}
#[inline]
fn add_part_from_package_with_id<P, T>(
&mut self,
source_package: &P,
part: &T,
relationship_id: impl Into<String>,
) -> Result<T, crate::common::SdkError>
where
P: SdkPackage,
T: SdkPart,
{
let relationship_id = relationship_id.into();
if crate::sdk::SdkPackage::storage(self).id()
== crate::sdk::SdkPackage::storage(source_package).id()
{
let part_id = part.part_id();
crate::sdk::SdkPackage::storage_mut(self)
.add_package_relationship_to_part(relationship_id.clone(), part_id)?;
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(self);
return Ok(T::from_relationship_id(relationship_id, part_id));
}
let (imported_part_id, added_count) = crate::sdk::SdkPackage::storage_mut(self)
.import_part_tree_from(
crate::sdk::SdkPackage::storage(source_package),
part.part_id(),
None,
relationship_id.clone(),
|part_id, _| crate::sdk::SdkPackage::part_bytes_for_copy(source_package, part_id),
)?;
for _ in 0..added_count {
crate::sdk::SdkPackage::push_root_element_slot(self);
}
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(self);
Ok(T::from_relationship_id(relationship_id, imported_part_id))
}
#[inline]
fn create_relationship_to_part<T: SdkPart>(
&mut self,
part: T,
) -> Result<String, crate::common::SdkError> {
if let Some(relationship_id) = self.get_id_of_part(&part) {
return Ok(relationship_id.to_string());
}
let relationship_id = crate::sdk::SdkPackage::relationships(self).next_relationship_id();
self.create_relationship_to_part_with_id(part, relationship_id)
}
#[inline]
fn create_relationship_to_part_with_id<T: SdkPart>(
&mut self,
part: T,
relationship_id: impl Into<String>,
) -> Result<String, crate::common::SdkError> {
let relationship_id = relationship_id.into();
crate::sdk::SdkPackage::storage_mut(self)
.add_package_relationship_to_part(relationship_id.clone(), part.part_id())?;
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(self);
Ok(relationship_id)
}
#[inline]
fn create_media_data_part(
&mut self,
content_type: impl Into<std::borrow::Cow<'static, str>>,
extension: impl AsRef<str>,
) -> Result<crate::common::MediaDataPart, crate::common::SdkError> {
let part_id = crate::sdk::SdkPackage::storage_mut(self)
.create_media_data_part(content_type.into().into_owned(), extension)?;
let path = crate::sdk::SdkPackage::storage(self)
.part(part_id)
.ok_or_else(|| {
crate::common::SdkError::CommonError(format!(
"part id {part_id:?} is not present in package storage"
))
})?
.path()
.to_string();
Ok(crate::common::MediaDataPart::from_part_id(
crate::sdk::SdkPackage::storage(self).id(),
part_id,
path,
))
}
#[inline]
fn create_media_data_part_with_content_type(
&mut self,
content_type: impl Into<std::borrow::Cow<'static, str>>,
) -> Result<crate::common::MediaDataPart, crate::common::SdkError> {
self.create_media_data_part(content_type, ".bin")
}
#[inline]
fn create_media_data_part_by_type(
&mut self,
part_type: MediaDataPartType,
) -> Result<crate::common::MediaDataPart, crate::common::SdkError> {
self.create_media_data_part(part_type.content_type(), part_type.extension())
}
#[inline]
fn add_new_part<T>(
&mut self,
relationship_id: impl Into<String>,
) -> Result<T, crate::common::SdkError>
where
T: SdkPart,
{
self.add_new_part_with_target_mode::<T>(
relationship_id,
crate::common::NewPartTargetMode::Indexed,
)
}
#[inline]
fn add_new_part_auto_id<T>(&mut self) -> Result<T, crate::common::SdkError>
where
T: SdkPart,
{
let relationship_id = crate::sdk::SdkPackage::relationships(self).next_relationship_id();
self.add_new_part::<T>(relationship_id)
}
#[inline]
fn add_new_part_with_content_type<T>(
&mut self,
relationship_id: impl Into<String>,
content_type: impl Into<std::borrow::Cow<'static, str>>,
) -> Result<T, crate::common::SdkError>
where
T: SdkPart,
{
self.add_new_part_with_content_type_and_extension::<T>(
relationship_id,
content_type,
T::EXTENSION,
crate::common::NewPartTargetMode::Indexed,
)
}
#[inline]
fn add_new_part_with_content_type_auto_id<T>(
&mut self,
content_type: impl Into<std::borrow::Cow<'static, str>>,
) -> Result<T, crate::common::SdkError>
where
T: SdkPart,
{
let relationship_id = crate::sdk::SdkPackage::relationships(self).next_relationship_id();
self.add_new_part_with_content_type::<T>(relationship_id, content_type)
}
#[inline]
fn add_new_part_with_target_mode<T>(
&mut self,
relationship_id: impl Into<String>,
target_mode: crate::common::NewPartTargetMode,
) -> Result<T, crate::common::SdkError>
where
T: SdkPart,
{
let relationship_id = relationship_id.into();
let content_type = typed_main_part_content_type::<T>(crate::sdk::SdkPackage::storage(self));
let part_id = crate::sdk::SdkPackage::storage_mut(self).add_package_part(
relationship_id.clone(),
crate::common::NewPartDescriptor {
relationship_type: std::borrow::Cow::Borrowed(T::RELATIONSHIP_TYPE),
content_type,
path_prefix: T::PATH_PREFIX,
target_name: T::TARGET_NAME,
extension: std::borrow::Cow::Borrowed(T::EXTENSION),
},
target_mode,
)?;
crate::sdk::SdkPackage::push_root_element_slot(self);
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(self);
Ok(T::from_relationship_id(relationship_id, part_id))
}
#[inline]
fn add_core_file_properties_part(
&mut self,
) -> Result<
crate::parts::core_file_properties_part::CoreFilePropertiesPart,
crate::common::SdkError,
> {
let relationship_id = crate::sdk::SdkPackage::relationships(self).next_relationship_id();
self.add_new_part_with_target_mode::<crate::parts::core_file_properties_part::CoreFilePropertiesPart>(
relationship_id,
crate::common::NewPartTargetMode::Fixed,
)
}
#[inline]
fn add_extended_file_properties_part(
&mut self,
) -> Result<
crate::parts::extended_file_properties_part::ExtendedFilePropertiesPart,
crate::common::SdkError,
> {
let relationship_id = crate::sdk::SdkPackage::relationships(self).next_relationship_id();
self.add_new_part_with_target_mode::<crate::parts::extended_file_properties_part::ExtendedFilePropertiesPart>(
relationship_id,
crate::common::NewPartTargetMode::Fixed,
)
}
#[inline]
fn add_custom_file_properties_part(
&mut self,
) -> Result<
crate::parts::custom_file_properties_part::CustomFilePropertiesPart,
crate::common::SdkError,
> {
let relationship_id = crate::sdk::SdkPackage::relationships(self).next_relationship_id();
self.add_new_part_with_target_mode::<crate::parts::custom_file_properties_part::CustomFilePropertiesPart>(
relationship_id,
crate::common::NewPartTargetMode::Fixed,
)
}
#[inline]
fn add_digital_signature_origin_part(
&mut self,
) -> Result<
crate::parts::digital_signature_origin_part::DigitalSignatureOriginPart,
crate::common::SdkError,
> {
let relationship_id = crate::sdk::SdkPackage::relationships(self).next_relationship_id();
self.add_new_part_with_target_mode::<crate::parts::digital_signature_origin_part::DigitalSignatureOriginPart>(
relationship_id,
crate::common::NewPartTargetMode::Fixed,
)
}
#[inline]
fn add_new_part_with_content_type_and_extension<T>(
&mut self,
relationship_id: impl Into<String>,
content_type: impl Into<std::borrow::Cow<'static, str>>,
extension: impl Into<std::borrow::Cow<'static, str>>,
target_mode: crate::common::NewPartTargetMode,
) -> Result<T, crate::common::SdkError>
where
T: SdkPart,
{
let relationship_id = relationship_id.into();
let part_id = crate::sdk::SdkPackage::storage_mut(self).add_package_part(
relationship_id.clone(),
crate::common::NewPartDescriptor {
relationship_type: std::borrow::Cow::Borrowed(T::RELATIONSHIP_TYPE),
content_type: content_type.into(),
path_prefix: T::PATH_PREFIX,
target_name: T::TARGET_NAME,
extension: extension.into(),
},
target_mode,
)?;
crate::sdk::SdkPackage::push_root_element_slot(self);
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(self);
Ok(T::from_relationship_id(relationship_id, part_id))
}
#[inline]
fn add_thumbnail_part(
&mut self,
content_type: impl Into<std::borrow::Cow<'static, str>>,
) -> Result<crate::parts::thumbnail_part::ThumbnailPart, crate::common::SdkError> {
self.add_new_part_with_content_type_auto_id::<crate::parts::thumbnail_part::ThumbnailPart>(
content_type,
)
}
#[inline]
fn add_thumbnail_part_with_id(
&mut self,
content_type: impl Into<std::borrow::Cow<'static, str>>,
relationship_id: impl Into<String>,
) -> Result<crate::parts::thumbnail_part::ThumbnailPart, crate::common::SdkError> {
self.add_new_part_with_content_type::<crate::parts::thumbnail_part::ThumbnailPart>(
relationship_id,
content_type,
)
}
#[inline]
fn add_thumbnail_part_by_type(
&mut self,
part_type: ThumbnailPartType,
) -> Result<crate::parts::thumbnail_part::ThumbnailPart, crate::common::SdkError> {
let relationship_id = crate::sdk::SdkPackage::relationships(self).next_relationship_id();
self.add_thumbnail_part_by_type_with_id(part_type, relationship_id)
}
#[inline]
fn add_thumbnail_part_by_type_with_id(
&mut self,
part_type: ThumbnailPartType,
relationship_id: impl Into<String>,
) -> Result<crate::parts::thumbnail_part::ThumbnailPart, crate::common::SdkError> {
self
.add_new_part_with_content_type_and_extension::<crate::parts::thumbnail_part::ThumbnailPart>(
relationship_id,
part_type.content_type(),
part_type.extension(),
crate::common::NewPartTargetMode::Indexed,
)
}
#[inline]
fn add_extended_part(
&mut self,
relationship_type: impl Into<String>,
content_type: impl Into<std::borrow::Cow<'static, str>>,
target_extension: impl Into<std::borrow::Cow<'static, str>>,
) -> Result<crate::parts::extended_part::ExtendedPart, crate::common::SdkError> {
let relationship_id = crate::sdk::SdkPackage::relationships(self).next_relationship_id();
self.add_extended_part_with_id(
relationship_type,
content_type,
target_extension,
relationship_id,
)
}
#[inline]
fn add_extended_part_with_id(
&mut self,
relationship_type: impl Into<String>,
content_type: impl Into<std::borrow::Cow<'static, str>>,
target_extension: impl Into<std::borrow::Cow<'static, str>>,
relationship_id: impl Into<String>,
) -> Result<crate::parts::extended_part::ExtendedPart, crate::common::SdkError> {
let relationship_id = relationship_id.into();
let part_id = crate::sdk::SdkPackage::storage_mut(self).add_package_part(
relationship_id.clone(),
crate::common::NewPartDescriptor {
relationship_type: std::borrow::Cow::Owned(relationship_type.into()),
content_type: content_type.into(),
path_prefix: "",
target_name: "extendedPart",
extension: target_extension.into(),
},
crate::common::NewPartTargetMode::Indexed,
)?;
crate::sdk::SdkPackage::push_root_element_slot(self);
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(self);
Ok(crate::parts::extended_part::ExtendedPart::from_relationship_id(relationship_id, part_id))
}
}
#[cfg(feature = "parts")]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RelatedPart<'a, T> {
relationship_id: &'a str,
relationship_type: &'a str,
part: T,
}
#[cfg(feature = "parts")]
impl<'a, T> RelatedPart<'a, T> {
#[inline]
pub const fn new(relationship_id: &'a str, relationship_type: &'a str, part: T) -> Self {
Self {
relationship_id,
relationship_type,
part,
}
}
#[inline]
pub const fn relationship_id(&self) -> &'a str {
self.relationship_id
}
#[inline]
pub const fn relationship_type(&self) -> &'a str {
self.relationship_type
}
#[inline]
pub const fn part(&self) -> &T {
&self.part
}
#[inline]
pub fn into_part(self) -> T {
self.part
}
}
#[cfg(feature = "parts")]
pub trait SdkPartDescriptor {
const RELATIONSHIP_TYPE: &'static str;
const PATH_PREFIX: &'static str;
const CONTENT_TYPE: &'static str;
const TARGET_NAME: &'static str;
const EXTENSION: &'static str;
}
#[cfg(feature = "parts")]
pub(crate) trait SdkPartRoot: SdkPart {
type RootElement;
fn wrap_root_element(root_element: Self::RootElement) -> crate::parts::PartRootElement;
fn root_element_ref(root_element: &crate::parts::PartRootElement) -> Option<&Self::RootElement>;
fn root_element_mut(
root_element: &mut crate::parts::PartRootElement,
) -> Option<&mut Self::RootElement>;
}
#[cfg(feature = "parts")]
pub trait SdkPart: SdkPartDescriptor + Clone + Sized + 'static {
fn from_part_id(part_id: crate::common::PartId) -> Self;
fn from_relationship_id(
relationship_id: impl Into<String>,
part_id: crate::common::PartId,
) -> Self;
fn set_relationship_id(&mut self, relationship_id: String);
fn part_id(&self) -> crate::common::PartId;
fn relationship_id(&self) -> Option<&str>;
#[inline]
fn missing_part_storage_error(&self) -> crate::common::SdkError {
crate::common::SdkError::CommonError(format!(
"part id {:?} is not present in package storage",
self.part_id()
))
}
#[inline]
fn next_relationship_id<P: SdkPackage>(
&self,
package: &P,
) -> Result<String, crate::common::SdkError> {
Ok(
crate::sdk::SdkPackage::storage(package)
.relationships(self.part_id())
.ok_or_else(|| self.missing_part_storage_error())?
.next_relationship_id(),
)
}
#[inline]
fn added_relationship_ref<'a, P: SdkPackage>(
&self,
package: &'a P,
relationship_id: &str,
) -> Result<crate::common::RelationshipRef<'a>, crate::common::SdkError> {
crate::sdk::SdkPackage::storage(package)
.relationships(self.part_id())
.and_then(|relationships| relationships.get(relationship_id))
.map(Into::into)
.ok_or_else(|| {
crate::common::SdkError::CommonError(format!(
"relationship id {relationship_id} is not present on part id {:?}",
self.part_id()
))
})
}
#[inline]
fn child_part_by_relationship_type<P, T>(&self, package: &P, relationship_type: &str) -> Option<T>
where
P: SdkPackage,
T: SdkPart,
{
self
.child_related_part_by_relationship_type(package, relationship_type)
.map(RelatedPart::into_part)
}
#[inline]
fn child_parts_by_relationship_type<'a, P, T>(
&'a self,
package: &'a P,
relationship_type: &'a str,
) -> impl Iterator<Item = T> + 'a
where
P: SdkPackage,
T: SdkPart,
{
self
.child_related_parts_by_relationship_type(package, relationship_type)
.map(RelatedPart::into_part)
}
#[inline]
fn related_part_of_type<'a, P, T>(&'a self, package: &'a P) -> Option<RelatedPart<'a, T>>
where
P: SdkPackage,
T: SdkPart,
{
self.child_related_parts_of_type(package).next()
}
#[inline]
fn related_parts_of_type<'a, P, T>(
&'a self,
package: &'a P,
) -> impl Iterator<Item = RelatedPart<'a, T>> + 'a
where
P: SdkPackage,
T: SdkPart,
{
self.child_related_parts_of_type(package)
}
#[inline]
fn child_related_parts_of_type<'a, P, T>(
&'a self,
package: &'a P,
) -> impl Iterator<Item = RelatedPart<'a, T>> + 'a
where
P: SdkPackage,
T: SdkPart,
{
crate::sdk::SdkPackage::storage(package)
.relationships(self.part_id())
.into_iter()
.flat_map(|relationships| relationships.iter())
.filter_map(move |relationship| {
let matches_type = crate::common::relationship_type_matches_bytes(
relationship.relationship_type_bytes(),
T::RELATIONSHIP_TYPE.as_bytes(),
);
matches_type
.then(|| relationship.target_part_id())
.flatten()
.map(|part_id| {
RelatedPart::new(
relationship.id(),
relationship.relationship_type(),
T::from_relationship_id(relationship.id(), part_id),
)
})
})
}
#[inline]
fn child_related_part_by_relationship_type<'a, P, T>(
&'a self,
package: &'a P,
relationship_type: &'a str,
) -> Option<RelatedPart<'a, T>>
where
P: SdkPackage,
T: SdkPart,
{
self
.child_related_parts_by_relationship_type(package, relationship_type)
.next()
}
#[inline]
fn child_related_parts_by_relationship_type<'a, P, T>(
&'a self,
package: &'a P,
relationship_type: &'a str,
) -> impl Iterator<Item = RelatedPart<'a, T>> + 'a
where
P: SdkPackage,
T: SdkPart,
{
crate::sdk::SdkPackage::storage(package)
.relationships(self.part_id())
.into_iter()
.flat_map(|relationships| relationships.iter())
.filter_map(move |relationship| {
crate::common::relationship_type_matches_bytes(
relationship.relationship_type_bytes(),
relationship_type.as_bytes(),
)
.then(|| relationship.target_part_id())
.flatten()
.map(|part_id| {
RelatedPart::new(
relationship.id(),
relationship.relationship_type(),
T::from_relationship_id(relationship.id(), part_id),
)
})
})
}
#[inline]
fn add_external_relationship<'a, P: SdkPackage>(
&self,
package: &'a mut P,
relationship_id: impl Into<String>,
relationship_type: impl Into<String>,
target: impl Into<String>,
) -> Result<crate::common::RelationshipRef<'a>, crate::common::SdkError> {
let relationship_id = relationship_id.into();
crate::sdk::SdkPackage::storage_mut(package)
.relationships_mut(self.part_id())
.ok_or_else(|| self.missing_part_storage_error())?
.add_external_relationship(relationship_id.clone(), relationship_type, target)?;
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(package);
self.added_relationship_ref(package, &relationship_id)
}
#[inline]
fn add_external_relationship_auto_id<'a, P: SdkPackage>(
&self,
package: &'a mut P,
relationship_type: impl Into<String>,
target: impl Into<String>,
) -> Result<crate::common::RelationshipRef<'a>, crate::common::SdkError> {
let relationship_id = self.next_relationship_id(package)?;
self.add_external_relationship(package, relationship_id, relationship_type, target)
}
#[inline]
fn add_hyperlink_relationship<'a, P: SdkPackage>(
&self,
package: &'a mut P,
relationship_id: impl Into<String>,
target: impl Into<String>,
) -> Result<crate::common::RelationshipRef<'a>, crate::common::SdkError> {
let relationship_id = relationship_id.into();
crate::sdk::SdkPackage::storage_mut(package)
.relationships_mut(self.part_id())
.ok_or_else(|| self.missing_part_storage_error())?
.add_hyperlink_relationship(relationship_id.clone(), target)?;
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(package);
self.added_relationship_ref(package, &relationship_id)
}
#[inline]
fn add_hyperlink_relationship_with_mode<'a, P: SdkPackage>(
&self,
package: &'a mut P,
relationship_id: impl Into<String>,
target: impl Into<String>,
target_mode: crate::schemas::opc_relationships::TargetMode,
) -> Result<crate::common::RelationshipRef<'a>, crate::common::SdkError> {
let relationship_id = relationship_id.into();
crate::sdk::SdkPackage::storage_mut(package)
.relationships_mut(self.part_id())
.ok_or_else(|| self.missing_part_storage_error())?
.add_hyperlink_relationship_with_mode(relationship_id.clone(), target, target_mode)?;
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(package);
self.added_relationship_ref(package, &relationship_id)
}
#[inline]
fn add_hyperlink_relationship_auto_id<'a, P: SdkPackage>(
&self,
package: &'a mut P,
target: impl Into<String>,
target_mode: crate::schemas::opc_relationships::TargetMode,
) -> Result<crate::common::RelationshipRef<'a>, crate::common::SdkError> {
let relationship_id = self.next_relationship_id(package)?;
self.add_hyperlink_relationship_with_mode(package, relationship_id, target, target_mode)
}
#[inline]
fn add_audio_reference_relationship<P: SdkPackage>(
&self,
package: &mut P,
media_data_part: &crate::common::MediaDataPart,
) -> Result<String, crate::common::SdkError> {
let relationship_id = self.next_relationship_id(package)?;
self.add_audio_reference_relationship_with_id(package, media_data_part, relationship_id)
}
#[inline]
fn add_audio_reference_relationship_with_id<P: SdkPackage>(
&self,
package: &mut P,
media_data_part: &crate::common::MediaDataPart,
relationship_id: impl Into<String>,
) -> Result<String, crate::common::SdkError> {
self.add_data_part_reference_relationship_with_id(
package,
media_data_part,
crate::common::RelationshipSet::AUDIO_REFERENCE_RELATIONSHIP_TYPE,
relationship_id,
)
}
#[inline]
fn add_media_reference_relationship<P: SdkPackage>(
&self,
package: &mut P,
media_data_part: &crate::common::MediaDataPart,
) -> Result<String, crate::common::SdkError> {
let relationship_id = self.next_relationship_id(package)?;
self.add_media_reference_relationship_with_id(package, media_data_part, relationship_id)
}
#[inline]
fn add_media_reference_relationship_with_id<P: SdkPackage>(
&self,
package: &mut P,
media_data_part: &crate::common::MediaDataPart,
relationship_id: impl Into<String>,
) -> Result<String, crate::common::SdkError> {
self.add_data_part_reference_relationship_with_id(
package,
media_data_part,
crate::common::RelationshipSet::MEDIA_REFERENCE_RELATIONSHIP_TYPE,
relationship_id,
)
}
#[inline]
fn add_video_reference_relationship<P: SdkPackage>(
&self,
package: &mut P,
media_data_part: &crate::common::MediaDataPart,
) -> Result<String, crate::common::SdkError> {
let relationship_id = self.next_relationship_id(package)?;
self.add_video_reference_relationship_with_id(package, media_data_part, relationship_id)
}
#[inline]
fn add_video_reference_relationship_with_id<P: SdkPackage>(
&self,
package: &mut P,
media_data_part: &crate::common::MediaDataPart,
relationship_id: impl Into<String>,
) -> Result<String, crate::common::SdkError> {
self.add_data_part_reference_relationship_with_id(
package,
media_data_part,
crate::common::RelationshipSet::VIDEO_REFERENCE_RELATIONSHIP_TYPE,
relationship_id,
)
}
#[inline]
fn add_data_part_reference_relationship_from_existing<P: SdkPackage>(
&self,
package: &mut P,
relationship: crate::common::Relationship,
) -> Result<String, crate::common::SdkError> {
use crate::common::ReferenceRelationshipKind;
if !relationship.is_reference_relationship()
|| !matches!(
relationship.reference_kind(),
Some(
ReferenceRelationshipKind::Audio
| ReferenceRelationshipKind::Media
| ReferenceRelationshipKind::Video
)
)
{
return Err(crate::common::SdkError::CommonError(format!(
"relationship id {} is not a data part reference relationship",
relationship.id()
)));
}
let target_part_id = relationship.target_part_id().ok_or_else(|| {
crate::common::SdkError::CommonError(format!(
"data part reference relationship id {} does not target a package part",
relationship.id()
))
})?;
let relationship_id = crate::sdk::SdkPackage::storage_mut(package)
.add_data_part_reference_relationship(
self.part_id(),
relationship.id(),
relationship.relationship_type(),
target_part_id,
)?;
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(package);
Ok(relationship_id)
}
#[inline]
fn add_data_part_reference_relationship_with_id<P: SdkPackage>(
&self,
package: &mut P,
media_data_part: &crate::common::MediaDataPart,
relationship_type: &str,
relationship_id: impl Into<String>,
) -> Result<String, crate::common::SdkError> {
let target_part_id = media_data_part.part_id_for_package(package)?;
let relationship_id = crate::sdk::SdkPackage::storage_mut(package)
.add_data_part_reference_relationship(
self.part_id(),
relationship_id,
relationship_type,
target_part_id,
)?;
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(package);
Ok(relationship_id)
}
#[inline]
fn add_new_part<P, T>(
&self,
package: &mut P,
relationship_id: impl Into<String>,
) -> Result<T, crate::common::SdkError>
where
P: SdkPackage,
T: SdkPart,
{
let relationship_id = relationship_id.into();
let part_id = crate::sdk::SdkPackage::storage_mut(package).add_child_part(
self.part_id(),
relationship_id.clone(),
crate::common::NewPartDescriptor {
relationship_type: std::borrow::Cow::Borrowed(T::RELATIONSHIP_TYPE),
content_type: std::borrow::Cow::Borrowed(T::CONTENT_TYPE),
path_prefix: T::PATH_PREFIX,
target_name: T::TARGET_NAME,
extension: std::borrow::Cow::Borrowed(T::EXTENSION),
},
)?;
crate::sdk::SdkPackage::push_root_element_slot(package);
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(package);
Ok(T::from_relationship_id(relationship_id, part_id))
}
#[inline]
fn add_new_part_with_content_type<P, T>(
&self,
package: &mut P,
relationship_id: impl Into<String>,
content_type: impl Into<std::borrow::Cow<'static, str>>,
) -> Result<T, crate::common::SdkError>
where
P: SdkPackage,
T: SdkPart,
{
let relationship_id = relationship_id.into();
let part_id = crate::sdk::SdkPackage::storage_mut(package).add_child_part(
self.part_id(),
relationship_id.clone(),
crate::common::NewPartDescriptor {
relationship_type: std::borrow::Cow::Borrowed(T::RELATIONSHIP_TYPE),
content_type: content_type.into(),
path_prefix: T::PATH_PREFIX,
target_name: T::TARGET_NAME,
extension: std::borrow::Cow::Borrowed(T::EXTENSION),
},
)?;
crate::sdk::SdkPackage::push_root_element_slot(package);
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(package);
Ok(T::from_relationship_id(relationship_id, part_id))
}
#[inline]
fn add_new_part_with_content_type_and_path<P, T>(
&self,
package: &mut P,
relationship_id: impl Into<String>,
content_type: impl Into<std::borrow::Cow<'static, str>>,
part_path: impl AsRef<str>,
) -> Result<T, crate::common::SdkError>
where
P: SdkPackage,
T: SdkPart,
{
let relationship_id = relationship_id.into();
let part_id = crate::sdk::SdkPackage::storage_mut(package).add_child_part_with_path(
self.part_id(),
relationship_id.clone(),
crate::common::NewPartDescriptor {
relationship_type: std::borrow::Cow::Borrowed(T::RELATIONSHIP_TYPE),
content_type: content_type.into(),
path_prefix: T::PATH_PREFIX,
target_name: T::TARGET_NAME,
extension: std::borrow::Cow::Borrowed(T::EXTENSION),
},
part_path,
)?;
crate::sdk::SdkPackage::push_root_element_slot(package);
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(package);
Ok(T::from_relationship_id(relationship_id, part_id))
}
#[inline]
fn add_new_part_auto_id<P, T>(&self, package: &mut P) -> Result<T, crate::common::SdkError>
where
P: SdkPackage,
T: SdkPart,
{
let relationship_id = self.next_relationship_id(package)?;
self.add_new_part::<P, T>(package, relationship_id)
}
#[inline]
fn add_new_part_with_content_type_auto_id<P, T>(
&self,
package: &mut P,
content_type: impl Into<std::borrow::Cow<'static, str>>,
) -> Result<T, crate::common::SdkError>
where
P: SdkPackage,
T: SdkPart,
{
let relationship_id = self.next_relationship_id(package)?;
self.add_new_part_with_content_type::<P, T>(package, relationship_id, content_type)
}
#[inline]
fn add_new_part_with_content_type_and_extension<P, T>(
&self,
package: &mut P,
relationship_id: impl Into<String>,
content_type: impl Into<std::borrow::Cow<'static, str>>,
extension: impl Into<std::borrow::Cow<'static, str>>,
) -> Result<T, crate::common::SdkError>
where
P: SdkPackage,
T: SdkPart,
{
let relationship_id = relationship_id.into();
let part_id = crate::sdk::SdkPackage::storage_mut(package).add_child_part(
self.part_id(),
relationship_id.clone(),
crate::common::NewPartDescriptor {
relationship_type: std::borrow::Cow::Borrowed(T::RELATIONSHIP_TYPE),
content_type: content_type.into(),
path_prefix: T::PATH_PREFIX,
target_name: T::TARGET_NAME,
extension: extension.into(),
},
)?;
crate::sdk::SdkPackage::push_root_element_slot(package);
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(package);
Ok(T::from_relationship_id(relationship_id, part_id))
}
#[inline]
fn add_new_part_with_content_type_and_extension_auto_id<P, T>(
&self,
package: &mut P,
content_type: impl Into<std::borrow::Cow<'static, str>>,
extension: impl Into<std::borrow::Cow<'static, str>>,
) -> Result<T, crate::common::SdkError>
where
P: SdkPackage,
T: SdkPart,
{
let relationship_id = self.next_relationship_id(package)?;
self.add_new_part_with_content_type_and_extension::<P, T>(
package,
relationship_id,
content_type,
extension,
)
}
#[inline]
fn add_extended_part<P>(
&self,
package: &mut P,
relationship_type: impl Into<String>,
content_type: impl Into<std::borrow::Cow<'static, str>>,
target_extension: impl Into<std::borrow::Cow<'static, str>>,
) -> Result<crate::parts::extended_part::ExtendedPart, crate::common::SdkError>
where
P: SdkPackage,
{
let relationship_id = self.next_relationship_id(package)?;
self.add_extended_part_with_id(
package,
relationship_type,
content_type,
target_extension,
relationship_id,
)
}
#[inline]
fn add_extended_part_with_id<P>(
&self,
package: &mut P,
relationship_type: impl Into<String>,
content_type: impl Into<std::borrow::Cow<'static, str>>,
target_extension: impl Into<std::borrow::Cow<'static, str>>,
relationship_id: impl Into<String>,
) -> Result<crate::parts::extended_part::ExtendedPart, crate::common::SdkError>
where
P: SdkPackage,
{
let relationship_id = relationship_id.into();
let part_id = crate::sdk::SdkPackage::storage_mut(package).add_child_part(
self.part_id(),
relationship_id.clone(),
crate::common::NewPartDescriptor {
relationship_type: std::borrow::Cow::Owned(relationship_type.into()),
content_type: content_type.into(),
path_prefix: ".",
target_name: "extendedPart",
extension: target_extension.into(),
},
)?;
crate::sdk::SdkPackage::push_root_element_slot(package);
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(package);
Ok(crate::parts::extended_part::ExtendedPart::from_relationship_id(relationship_id, part_id))
}
#[inline]
fn add_image_part<P>(
&self,
package: &mut P,
content_type: impl Into<std::borrow::Cow<'static, str>>,
) -> Result<crate::parts::image_part::ImagePart, crate::common::SdkError>
where
P: SdkPackage,
{
let content_type = content_type.into();
let extension =
extension_for_content_type::<crate::parts::image_part::ImagePart>(content_type.as_ref());
self.add_new_part_with_content_type_and_extension_auto_id::<
P,
crate::parts::image_part::ImagePart,
>(
package,
content_type,
extension,
)
}
#[inline]
fn add_image_part_with_id<P>(
&self,
package: &mut P,
content_type: impl Into<std::borrow::Cow<'static, str>>,
relationship_id: impl Into<String>,
) -> Result<crate::parts::image_part::ImagePart, crate::common::SdkError>
where
P: SdkPackage,
{
let content_type = content_type.into();
let extension =
extension_for_content_type::<crate::parts::image_part::ImagePart>(content_type.as_ref());
self.add_new_part_with_content_type_and_extension::<P, crate::parts::image_part::ImagePart>(
package,
relationship_id,
content_type,
extension,
)
}
#[inline]
fn add_alternative_format_import_part<P>(
&self,
package: &mut P,
content_type: impl Into<std::borrow::Cow<'static, str>>,
) -> Result<
crate::parts::alternative_format_import_part::AlternativeFormatImportPart,
crate::common::SdkError,
>
where
P: SdkPackage,
{
self.add_new_part_with_content_type_auto_id::<
P,
crate::parts::alternative_format_import_part::AlternativeFormatImportPart,
>(package, content_type)
}
#[inline]
fn add_alternative_format_import_part_with_id<P>(
&self,
package: &mut P,
content_type: impl Into<std::borrow::Cow<'static, str>>,
relationship_id: impl Into<String>,
) -> Result<
crate::parts::alternative_format_import_part::AlternativeFormatImportPart,
crate::common::SdkError,
>
where
P: SdkPackage,
{
self.add_new_part_with_content_type::<P, crate::parts::alternative_format_import_part::AlternativeFormatImportPart>(
package,
relationship_id,
content_type,
)
}
#[inline]
fn add_alternative_format_import_part_by_type<P>(
&self,
package: &mut P,
part_type: AlternativeFormatImportPartType,
) -> Result<
crate::parts::alternative_format_import_part::AlternativeFormatImportPart,
crate::common::SdkError,
>
where
P: SdkPackage,
{
self.add_alternative_format_import_part(package, part_type.content_type())
}
#[inline]
fn add_alternative_format_import_part_by_type_with_id<P>(
&self,
package: &mut P,
part_type: AlternativeFormatImportPartType,
relationship_id: impl Into<String>,
) -> Result<
crate::parts::alternative_format_import_part::AlternativeFormatImportPart,
crate::common::SdkError,
>
where
P: SdkPackage,
{
self.add_alternative_format_import_part_with_id(
package,
part_type.content_type(),
relationship_id,
)
}
#[inline]
fn add_custom_xml_part<P>(
&self,
package: &mut P,
content_type: impl Into<std::borrow::Cow<'static, str>>,
) -> Result<crate::parts::custom_xml_part::CustomXmlPart, crate::common::SdkError>
where
P: SdkPackage,
{
self.add_new_part_with_content_type_auto_id::<P, crate::parts::custom_xml_part::CustomXmlPart>(
package,
content_type,
)
}
#[inline]
fn add_custom_xml_part_with_id<P>(
&self,
package: &mut P,
content_type: impl Into<std::borrow::Cow<'static, str>>,
relationship_id: impl Into<String>,
) -> Result<crate::parts::custom_xml_part::CustomXmlPart, crate::common::SdkError>
where
P: SdkPackage,
{
self.add_new_part_with_content_type::<P, crate::parts::custom_xml_part::CustomXmlPart>(
package,
relationship_id,
content_type,
)
}
#[inline]
fn add_custom_xml_part_by_type<P>(
&self,
package: &mut P,
part_type: CustomXmlPartType,
) -> Result<crate::parts::custom_xml_part::CustomXmlPart, crate::common::SdkError>
where
P: SdkPackage,
{
self.add_custom_xml_part(package, part_type.content_type())
}
#[inline]
fn add_custom_xml_part_by_type_with_id<P>(
&self,
package: &mut P,
part_type: CustomXmlPartType,
relationship_id: impl Into<String>,
) -> Result<crate::parts::custom_xml_part::CustomXmlPart, crate::common::SdkError>
where
P: SdkPackage,
{
self.add_custom_xml_part_with_id(package, part_type.content_type(), relationship_id)
}
#[inline]
fn add_custom_property_part<P>(
&self,
package: &mut P,
content_type: impl Into<std::borrow::Cow<'static, str>>,
) -> Result<crate::parts::custom_property_part::CustomPropertyPart, crate::common::SdkError>
where
P: SdkPackage,
{
self.add_new_part_with_content_type_auto_id::<P, crate::parts::custom_property_part::CustomPropertyPart>(
package,
content_type,
)
}
#[inline]
fn add_custom_property_part_with_id<P>(
&self,
package: &mut P,
content_type: impl Into<std::borrow::Cow<'static, str>>,
relationship_id: impl Into<String>,
) -> Result<crate::parts::custom_property_part::CustomPropertyPart, crate::common::SdkError>
where
P: SdkPackage,
{
self
.add_new_part_with_content_type::<P, crate::parts::custom_property_part::CustomPropertyPart>(
package,
relationship_id,
content_type,
)
}
#[inline]
fn add_custom_property_part_by_type<P>(
&self,
package: &mut P,
part_type: CustomPropertyPartType,
) -> Result<crate::parts::custom_property_part::CustomPropertyPart, crate::common::SdkError>
where
P: SdkPackage,
{
self.add_new_part_with_content_type_and_extension_auto_id::<
P,
crate::parts::custom_property_part::CustomPropertyPart,
>(package, part_type.content_type(), part_type.extension())
}
#[inline]
fn add_custom_property_part_by_type_with_id<P>(
&self,
package: &mut P,
part_type: CustomPropertyPartType,
relationship_id: impl Into<String>,
) -> Result<crate::parts::custom_property_part::CustomPropertyPart, crate::common::SdkError>
where
P: SdkPackage,
{
self.add_new_part_with_content_type_and_extension::<P, crate::parts::custom_property_part::CustomPropertyPart>(
package,
relationship_id,
part_type.content_type(),
part_type.extension(),
)
}
#[inline]
fn add_embedded_object_part<P>(
&self,
package: &mut P,
content_type: impl Into<std::borrow::Cow<'static, str>>,
) -> Result<crate::parts::embedded_object_part::EmbeddedObjectPart, crate::common::SdkError>
where
P: SdkPackage,
{
self.add_new_part_with_content_type_auto_id::<P, crate::parts::embedded_object_part::EmbeddedObjectPart>(
package,
content_type,
)
}
#[inline]
fn add_embedded_object_part_with_id<P>(
&self,
package: &mut P,
content_type: impl Into<std::borrow::Cow<'static, str>>,
relationship_id: impl Into<String>,
) -> Result<crate::parts::embedded_object_part::EmbeddedObjectPart, crate::common::SdkError>
where
P: SdkPackage,
{
self
.add_new_part_with_content_type::<P, crate::parts::embedded_object_part::EmbeddedObjectPart>(
package,
relationship_id,
content_type,
)
}
#[inline]
fn add_embedded_object_part_by_type<P>(
&self,
package: &mut P,
part_type: EmbeddedObjectPartType,
) -> Result<crate::parts::embedded_object_part::EmbeddedObjectPart, crate::common::SdkError>
where
P: SdkPackage,
{
self.add_new_part_with_content_type_and_extension_auto_id::<
P,
crate::parts::embedded_object_part::EmbeddedObjectPart,
>(package, part_type.content_type(), part_type.extension())
}
#[inline]
fn add_embedded_object_part_by_type_with_id<P>(
&self,
package: &mut P,
part_type: EmbeddedObjectPartType,
relationship_id: impl Into<String>,
) -> Result<crate::parts::embedded_object_part::EmbeddedObjectPart, crate::common::SdkError>
where
P: SdkPackage,
{
self.add_new_part_with_content_type_and_extension::<P, crate::parts::embedded_object_part::EmbeddedObjectPart>(
package,
relationship_id,
part_type.content_type(),
part_type.extension(),
)
}
#[inline]
fn add_embedded_package_part<P>(
&self,
package: &mut P,
content_type: impl Into<std::borrow::Cow<'static, str>>,
) -> Result<crate::parts::embedded_package_part::EmbeddedPackagePart, crate::common::SdkError>
where
P: SdkPackage,
{
self.add_new_part_with_content_type_auto_id::<P, crate::parts::embedded_package_part::EmbeddedPackagePart>(
package,
content_type,
)
}
#[inline]
fn add_embedded_package_part_with_id<P>(
&self,
package: &mut P,
content_type: impl Into<std::borrow::Cow<'static, str>>,
relationship_id: impl Into<String>,
) -> Result<crate::parts::embedded_package_part::EmbeddedPackagePart, crate::common::SdkError>
where
P: SdkPackage,
{
self.add_new_part_with_content_type::<P, crate::parts::embedded_package_part::EmbeddedPackagePart>(
package,
relationship_id,
content_type,
)
}
#[inline]
fn add_embedded_package_part_by_type<P>(
&self,
package: &mut P,
part_type: EmbeddedPackagePartType,
) -> Result<crate::parts::embedded_package_part::EmbeddedPackagePart, crate::common::SdkError>
where
P: SdkPackage,
{
self.add_new_part_with_content_type_and_extension_auto_id::<
P,
crate::parts::embedded_package_part::EmbeddedPackagePart,
>(package, part_type.content_type(), part_type.extension())
}
#[inline]
fn add_embedded_package_part_by_type_with_id<P>(
&self,
package: &mut P,
part_type: EmbeddedPackagePartType,
relationship_id: impl Into<String>,
) -> Result<crate::parts::embedded_package_part::EmbeddedPackagePart, crate::common::SdkError>
where
P: SdkPackage,
{
self.add_new_part_with_content_type_and_extension::<P, crate::parts::embedded_package_part::EmbeddedPackagePart>(
package,
relationship_id,
part_type.content_type(),
part_type.extension(),
)
}
#[inline]
fn add_font_part<P>(
&self,
package: &mut P,
content_type: impl Into<std::borrow::Cow<'static, str>>,
) -> Result<crate::parts::font_part::FontPart, crate::common::SdkError>
where
P: SdkPackage,
{
self.add_new_part_with_content_type_auto_id::<P, crate::parts::font_part::FontPart>(
package,
content_type,
)
}
#[inline]
fn add_font_part_with_id<P>(
&self,
package: &mut P,
content_type: impl Into<std::borrow::Cow<'static, str>>,
relationship_id: impl Into<String>,
) -> Result<crate::parts::font_part::FontPart, crate::common::SdkError>
where
P: SdkPackage,
{
self.add_new_part_with_content_type::<P, crate::parts::font_part::FontPart>(
package,
relationship_id,
content_type,
)
}
#[inline]
fn add_font_part_by_type<P>(
&self,
package: &mut P,
part_type: FontPartType,
) -> Result<crate::parts::font_part::FontPart, crate::common::SdkError>
where
P: SdkPackage,
{
self
.add_new_part_with_content_type_and_extension_auto_id::<P, crate::parts::font_part::FontPart>(
package,
part_type.content_type(),
part_type.extension(),
)
}
#[inline]
fn add_font_part_by_type_with_id<P>(
&self,
package: &mut P,
part_type: FontPartType,
relationship_id: impl Into<String>,
) -> Result<crate::parts::font_part::FontPart, crate::common::SdkError>
where
P: SdkPackage,
{
self.add_new_part_with_content_type_and_extension::<P, crate::parts::font_part::FontPart>(
package,
relationship_id,
part_type.content_type(),
part_type.extension(),
)
}
#[inline]
fn add_mail_merge_recipient_data_part<P>(
&self,
package: &mut P,
content_type: impl Into<std::borrow::Cow<'static, str>>,
) -> Result<
crate::parts::mail_merge_recipient_data_part::MailMergeRecipientDataPart,
crate::common::SdkError,
>
where
P: SdkPackage,
{
self.add_new_part_with_content_type_auto_id::<
P,
crate::parts::mail_merge_recipient_data_part::MailMergeRecipientDataPart,
>(package, content_type)
}
#[inline]
fn add_mail_merge_recipient_data_part_with_id<P>(
&self,
package: &mut P,
content_type: impl Into<std::borrow::Cow<'static, str>>,
relationship_id: impl Into<String>,
) -> Result<
crate::parts::mail_merge_recipient_data_part::MailMergeRecipientDataPart,
crate::common::SdkError,
>
where
P: SdkPackage,
{
self.add_new_part_with_content_type::<P, crate::parts::mail_merge_recipient_data_part::MailMergeRecipientDataPart>(
package,
relationship_id,
content_type,
)
}
#[inline]
fn add_mail_merge_recipient_data_part_by_type<P>(
&self,
package: &mut P,
part_type: MailMergeRecipientDataPartType,
) -> Result<
crate::parts::mail_merge_recipient_data_part::MailMergeRecipientDataPart,
crate::common::SdkError,
>
where
P: SdkPackage,
{
self.add_new_part_with_content_type_and_extension_auto_id::<
P,
crate::parts::mail_merge_recipient_data_part::MailMergeRecipientDataPart,
>(package, part_type.content_type(), part_type.extension())
}
#[inline]
fn add_mail_merge_recipient_data_part_by_type_with_id<P>(
&self,
package: &mut P,
part_type: MailMergeRecipientDataPartType,
relationship_id: impl Into<String>,
) -> Result<
crate::parts::mail_merge_recipient_data_part::MailMergeRecipientDataPart,
crate::common::SdkError,
>
where
P: SdkPackage,
{
self.add_new_part_with_content_type_and_extension::<
P,
crate::parts::mail_merge_recipient_data_part::MailMergeRecipientDataPart,
>(
package,
relationship_id,
part_type.content_type(),
part_type.extension(),
)
}
#[inline]
fn add_embedded_control_persistence_binary_data_part<P>(
&self,
package: &mut P,
content_type: impl Into<std::borrow::Cow<'static, str>>,
) -> Result<
crate::parts::embedded_control_persistence_binary_data_part::EmbeddedControlPersistenceBinaryDataPart,
crate::common::SdkError,
>
where
P: SdkPackage,
{
self.add_new_part_with_content_type_auto_id::<
P,
crate::parts::embedded_control_persistence_binary_data_part::EmbeddedControlPersistenceBinaryDataPart,
>(package, content_type)
}
#[inline]
fn add_embedded_control_persistence_binary_data_part_with_id<P>(
&self,
package: &mut P,
content_type: impl Into<std::borrow::Cow<'static, str>>,
relationship_id: impl Into<String>,
) -> Result<
crate::parts::embedded_control_persistence_binary_data_part::EmbeddedControlPersistenceBinaryDataPart,
crate::common::SdkError,
>
where
P: SdkPackage,
{
self.add_new_part_with_content_type::<
P,
crate::parts::embedded_control_persistence_binary_data_part::EmbeddedControlPersistenceBinaryDataPart,
>(package, relationship_id, content_type)
}
#[inline]
fn add_embedded_control_persistence_binary_data_part_by_type<P>(
&self,
package: &mut P,
part_type: EmbeddedControlPersistenceBinaryDataPartType,
) -> Result<
crate::parts::embedded_control_persistence_binary_data_part::EmbeddedControlPersistenceBinaryDataPart,
crate::common::SdkError,
>
where
P: SdkPackage,
{
self.add_new_part_with_content_type_and_extension_auto_id::<
P,
crate::parts::embedded_control_persistence_binary_data_part::EmbeddedControlPersistenceBinaryDataPart,
>(package, part_type.content_type(), part_type.extension())
}
#[inline]
fn add_embedded_control_persistence_binary_data_part_by_type_with_id<P>(
&self,
package: &mut P,
part_type: EmbeddedControlPersistenceBinaryDataPartType,
relationship_id: impl Into<String>,
) -> Result<
crate::parts::embedded_control_persistence_binary_data_part::EmbeddedControlPersistenceBinaryDataPart,
crate::common::SdkError,
>
where
P: SdkPackage,
{
self.add_new_part_with_content_type_and_extension::<
P,
crate::parts::embedded_control_persistence_binary_data_part::EmbeddedControlPersistenceBinaryDataPart,
>(
package,
relationship_id,
part_type.content_type(),
part_type.extension(),
)
}
#[inline]
fn add_embedded_control_persistence_part<P>(
&self,
package: &mut P,
content_type: impl Into<std::borrow::Cow<'static, str>>,
) -> Result<
crate::parts::embedded_control_persistence_part::EmbeddedControlPersistencePart,
crate::common::SdkError,
>
where
P: SdkPackage,
{
self.add_new_part_with_content_type_auto_id::<
P,
crate::parts::embedded_control_persistence_part::EmbeddedControlPersistencePart,
>(package, content_type)
}
#[inline]
fn add_embedded_control_persistence_part_with_id<P>(
&self,
package: &mut P,
content_type: impl Into<std::borrow::Cow<'static, str>>,
relationship_id: impl Into<String>,
) -> Result<
crate::parts::embedded_control_persistence_part::EmbeddedControlPersistencePart,
crate::common::SdkError,
>
where
P: SdkPackage,
{
self.add_new_part_with_content_type::<
P,
crate::parts::embedded_control_persistence_part::EmbeddedControlPersistencePart,
>(package, relationship_id, content_type)
}
#[inline]
fn add_embedded_control_persistence_part_by_type<P>(
&self,
package: &mut P,
part_type: EmbeddedControlPersistencePartType,
) -> Result<
crate::parts::embedded_control_persistence_part::EmbeddedControlPersistencePart,
crate::common::SdkError,
>
where
P: SdkPackage,
{
self.add_new_part_with_content_type_and_extension_auto_id::<
P,
crate::parts::embedded_control_persistence_part::EmbeddedControlPersistencePart,
>(package, part_type.content_type(), part_type.extension())
}
#[inline]
fn add_embedded_control_persistence_part_by_type_with_id<P>(
&self,
package: &mut P,
part_type: EmbeddedControlPersistencePartType,
relationship_id: impl Into<String>,
) -> Result<
crate::parts::embedded_control_persistence_part::EmbeddedControlPersistencePart,
crate::common::SdkError,
>
where
P: SdkPackage,
{
self.add_new_part_with_content_type_and_extension::<
P,
crate::parts::embedded_control_persistence_part::EmbeddedControlPersistencePart,
>(
package,
relationship_id,
part_type.content_type(),
part_type.extension(),
)
}
#[inline]
fn get_reference_relationship<'a, P: SdkPackage>(
&'a self,
package: &'a P,
relationship_id: &str,
) -> Option<crate::common::RelationshipRef<'a>> {
crate::sdk::SdkPackage::storage(package)
.relationships(self.part_id())?
.get(relationship_id)
.filter(|relationship| relationship.is_reference_relationship())
.map(Into::into)
}
#[inline]
fn get_external_relationship<'a, P: SdkPackage>(
&'a self,
package: &'a P,
relationship_id: &str,
) -> Option<crate::common::RelationshipRef<'a>> {
crate::sdk::SdkPackage::storage(package)
.relationships(self.part_id())?
.get_external_relationship(relationship_id)
.map(Into::into)
}
#[inline]
fn get_hyperlink_relationship<'a, P: SdkPackage>(
&'a self,
package: &'a P,
relationship_id: &str,
) -> Option<crate::common::RelationshipRef<'a>> {
crate::sdk::SdkPackage::storage(package)
.relationships(self.part_id())?
.get_hyperlink_relationship(relationship_id)
.map(Into::into)
}
#[inline]
fn delete_reference_relationship<P: SdkPackage>(
&self,
package: &mut P,
relationship_id: &str,
) -> Result<crate::common::Relationship, crate::common::SdkError> {
let relationship = crate::sdk::SdkPackage::storage_mut(package)
.relationships_mut(self.part_id())
.ok_or_else(|| self.missing_part_storage_error())?
.remove_reference_relationship(relationship_id)?;
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(package);
Ok(relationship.into())
}
#[inline]
fn delete_external_relationship<P: SdkPackage>(
&self,
package: &mut P,
relationship_id: &str,
) -> Result<crate::common::Relationship, crate::common::SdkError> {
let relationship = crate::sdk::SdkPackage::storage_mut(package)
.relationships_mut(self.part_id())
.ok_or_else(|| self.missing_part_storage_error())?
.remove_external_relationship(relationship_id)?;
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(package);
Ok(relationship.into())
}
#[inline]
fn change_relationship_id<P: SdkPackage>(
&self,
package: &mut P,
relationship_id: &str,
new_relationship_id: impl Into<String>,
) -> Result<(), crate::common::SdkError> {
crate::sdk::SdkPackage::storage_mut(package)
.relationships_mut(self.part_id())
.ok_or_else(|| self.missing_part_storage_error())?
.change_relationship_id(relationship_id, new_relationship_id)?;
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(package);
Ok(())
}
#[inline]
fn external_relationships<'a, P: SdkPackage>(
&'a self,
package: &'a P,
) -> impl Iterator<Item = crate::common::RelationshipRef<'a>> {
crate::sdk::SdkPackage::storage(package)
.relationships(self.part_id())
.into_iter()
.flat_map(crate::common::RelationshipSet::external_relationships)
.map(Into::into)
}
#[inline]
fn hyperlink_relationships<'a, P: SdkPackage>(
&'a self,
package: &'a P,
) -> impl Iterator<Item = crate::common::RelationshipRef<'a>> {
crate::sdk::SdkPackage::storage(package)
.relationships(self.part_id())
.into_iter()
.flat_map(crate::common::RelationshipSet::hyperlink_relationships)
.map(Into::into)
}
#[inline]
fn data_part_reference_relationships<'a, P: SdkPackage>(
&'a self,
package: &'a P,
) -> impl Iterator<Item = crate::common::RelationshipRef<'a>> {
crate::sdk::SdkPackage::storage(package)
.relationships(self.part_id())
.into_iter()
.flat_map(crate::common::RelationshipSet::data_part_reference_relationships)
.map(Into::into)
}
#[inline]
fn path<'a, P: SdkPackage>(&self, package: &'a P) -> Option<&'a str> {
crate::sdk::SdkPackage::storage(package)
.part(self.part_id())
.map(crate::common::StoredPart::path)
}
#[inline]
fn content_type<'a, P: SdkPackage>(&self, package: &'a P) -> Option<&'a str> {
crate::sdk::SdkPackage::storage(package)
.part(self.part_id())
.map(|part| {
if Self::CONTENT_TYPE == "model/gltf-binary" && part.content_type() == "model/gltf.binary" {
Self::CONTENT_TYPE
} else {
part.content_type()
}
})
}
#[inline]
fn data<'a, P: SdkPackage>(&self, package: &'a P) -> Option<&'a [u8]> {
crate::sdk::SdkPackage::storage(package)
.part(self.part_id())
.map(|part| part.data().bytes())
}
#[inline]
fn data_to_vec<P: SdkPackage>(&self, package: &P) -> Option<Vec<u8>> {
self.data(package).map(<[u8]>::to_vec)
}
#[inline]
fn data_as_str<'a, P: SdkPackage>(
&self,
package: &'a P,
) -> Result<Option<&'a str>, crate::common::SdkError> {
self
.data(package)
.map(std::str::from_utf8)
.transpose()
.map_err(|error| crate::common::SdkError::CommonError(error.to_string()))
}
#[inline]
fn write_data_to<P: SdkPackage, W: std::io::Write>(
&self,
package: &P,
writer: &mut W,
) -> Result<bool, crate::common::SdkError> {
let Some(data) = self.data(package) else {
return Ok(false);
};
writer.write_all(data)?;
Ok(true)
}
#[inline]
fn set_data<P: SdkPackage>(
&self,
package: &mut P,
data: impl Into<Vec<u8>>,
) -> Result<(), crate::common::SdkError> {
crate::sdk::SdkPackage::storage_mut(package).set_part_data(self.part_id(), data)
}
#[inline]
fn feed_data<P: SdkPackage, R: std::io::Read>(
&self,
package: &mut P,
reader: &mut R,
) -> Result<(), crate::common::SdkError> {
crate::sdk::SdkPackage::storage_mut(package).feed_part_data(self.part_id(), reader)
}
#[inline]
fn target_part_id<P: SdkPackage>(
&self,
package: &P,
relationship_id: &str,
) -> Option<crate::common::PartId> {
crate::sdk::SdkPackage::storage(package).target_part_id(self.part_id(), relationship_id)
}
#[inline]
fn parts<'a, P: SdkPackage + Sized>(
&'a self,
package: &'a P,
) -> impl Iterator<Item = crate::parts::IdPartPair<'a>> + 'a {
crate::sdk::SdkPackage::storage(package)
.relationships(self.part_id())
.into_iter()
.flat_map(|relationships| relationships.iter())
.filter_map(move |relationship| {
let part_id = relationship.target_part_id()?;
let part = crate::parts::PartRef::from_part_id(package, part_id)?;
Some(crate::parts::IdPartPair::new(relationship.id(), part))
})
}
#[inline]
fn get_all_parts<'a, P: SdkPackage + Sized>(
&'a self,
package: &'a P,
) -> impl Iterator<Item = crate::parts::PartRef> + 'a {
let Some(relationships) =
crate::sdk::SdkPackage::storage(package).relationships(self.part_id())
else {
return Vec::new().into_iter();
};
collect_all_parts_from_relationships(package, relationships).into_iter()
}
#[inline]
fn get_parent_parts<'a, P: SdkPackage + Sized>(
&'a self,
package: &'a P,
) -> impl Iterator<Item = crate::parts::PartRef> + 'a {
let target_part_id = self.part_id();
package
.get_all_parts()
.filter(move |part| {
crate::sdk::SdkPackage::storage(package)
.relationships(part.part_id())
.is_some_and(|relationships| {
relationships
.part_relationships()
.any(|relationship| relationship.target_part_id() == Some(target_part_id))
})
})
.collect::<Vec<_>>()
.into_iter()
}
#[inline]
fn get_part_by_id<P: SdkPackage + Sized>(
&self,
package: &P,
relationship_id: &str,
) -> Option<crate::parts::PartRef> {
let part_id = self.target_part_id(package, relationship_id)?;
crate::parts::PartRef::from_part_id(package, part_id)
}
#[inline]
fn get_part_by_id_required<P: SdkPackage + Sized>(
&self,
package: &P,
relationship_id: &str,
) -> Result<crate::parts::PartRef, crate::common::SdkError> {
self
.get_part_by_id(package, relationship_id)
.ok_or_else(|| {
crate::common::SdkError::CommonError(format!(
"part relationship id {relationship_id} does not exist"
))
})
}
#[inline]
fn try_get_part_by_id<P: SdkPackage + Sized>(
&self,
package: &P,
relationship_id: &str,
) -> Option<crate::parts::PartRef> {
self.get_part_by_id(package, relationship_id)
}
#[inline]
fn get_parts_of_type<'a, P: SdkPackage + Sized, T: SdkPart>(
&'a self,
package: &'a P,
) -> impl Iterator<Item = T> + 'a {
let storage = crate::sdk::SdkPackage::storage(package);
crate::sdk::SdkPackage::storage(package)
.relationships(self.part_id())
.into_iter()
.flat_map(|relationships| relationships.part_relationships())
.filter_map(move |relationship| relationship_target_as_part::<T>(storage, relationship))
}
#[inline]
fn get_id_of_part<'a, P: SdkPackage, T: SdkPart>(
&'a self,
package: &'a P,
part: &T,
) -> Option<&'a str> {
let target_part_id = part.part_id();
crate::sdk::SdkPackage::storage(package)
.relationships(self.part_id())?
.iter()
.find_map(|relationship| {
(relationship.target_part_id() == Some(target_part_id)).then_some(relationship.id())
})
}
#[inline]
fn get_id_of_part_required<'a, P: SdkPackage, T: SdkPart>(
&'a self,
package: &'a P,
part: &T,
) -> Result<&'a str, crate::common::SdkError> {
self.get_id_of_part(package, part).ok_or_else(|| {
crate::common::SdkError::CommonError(format!(
"part id {:?} is not referenced by part id {:?}",
part.part_id(),
self.part_id()
))
})
}
#[inline]
fn change_id_of_part<P: SdkPackage, T: SdkPart>(
&self,
package: &mut P,
part: &T,
new_relationship_id: impl Into<String>,
) -> Result<String, crate::common::SdkError> {
let old_relationship_id = self.get_id_of_part_required(package, part)?.to_string();
self.change_relationship_id(package, &old_relationship_id, new_relationship_id)?;
Ok(old_relationship_id)
}
#[inline]
fn delete_part_by_id<P: SdkPackage>(
&self,
package: &mut P,
relationship_id: &str,
) -> Result<bool, crate::common::SdkError> {
let deleted = crate::sdk::SdkPackage::storage_mut(package)
.delete_child_part(self.part_id(), relationship_id)?;
if deleted {
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(package);
}
Ok(deleted)
}
#[inline]
fn delete_part<P: SdkPackage, T: SdkPart>(
&self,
package: &mut P,
part: T,
) -> Result<bool, crate::common::SdkError> {
let Some(relationship_id) = self.get_id_of_part(package, &part).map(str::to_string) else {
return Ok(false);
};
self.delete_part_by_id(package, &relationship_id)
}
#[inline]
fn delete_parts<P, T, I>(&self, package: &mut P, parts: I) -> Result<(), crate::common::SdkError>
where
P: SdkPackage,
T: SdkPart,
I: IntoIterator<Item = T>,
{
let relationship_ids: Vec<_> = parts
.into_iter()
.filter_map(|part| self.get_id_of_part(package, &part).map(str::to_string))
.collect();
for relationship_id in relationship_ids {
self.delete_part_by_id(package, &relationship_id)?;
}
Ok(())
}
#[inline]
fn add_part<P: SdkPackage, T: SdkPart>(
&self,
package: &mut P,
part: T,
) -> Result<T, crate::common::SdkError> {
if self.get_id_of_part(package, &part).is_some() {
return Ok(part);
}
let relationship_id = self.next_relationship_id(package)?;
self.add_part_with_id(package, part, relationship_id)
}
#[inline]
fn add_part_with_id<P: SdkPackage, T: SdkPart>(
&self,
package: &mut P,
part: T,
relationship_id: impl Into<String>,
) -> Result<T, crate::common::SdkError> {
let relationship_id = relationship_id.into();
let part_id = part.part_id();
crate::sdk::SdkPackage::storage_mut(package).add_child_relationship_to_part(
self.part_id(),
relationship_id.clone(),
part_id,
)?;
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(package);
Ok(T::from_relationship_id(relationship_id, part_id))
}
#[inline]
fn add_part_from_package<P, S, T>(
&self,
package: &mut P,
source_package: &S,
part: &T,
) -> Result<T, crate::common::SdkError>
where
P: SdkPackage,
S: SdkPackage,
T: SdkPart,
{
let relationship_id = part
.relationship_id()
.map(str::to_string)
.unwrap_or_else(|| {
crate::sdk::SdkPackage::storage(package)
.relationships(self.part_id())
.map(crate::common::RelationshipSet::next_relationship_id)
.unwrap_or_else(|| "rId1".to_string())
});
self.add_part_from_package_with_id(package, source_package, part, relationship_id)
}
#[inline]
fn add_part_from_package_with_id<P, S, T>(
&self,
package: &mut P,
source_package: &S,
part: &T,
relationship_id: impl Into<String>,
) -> Result<T, crate::common::SdkError>
where
P: SdkPackage,
S: SdkPackage,
T: SdkPart,
{
let relationship_id = relationship_id.into();
if crate::sdk::SdkPackage::storage(package).id()
== crate::sdk::SdkPackage::storage(source_package).id()
{
let part_id = part.part_id();
crate::sdk::SdkPackage::storage_mut(package).add_child_relationship_to_part(
self.part_id(),
relationship_id.clone(),
part_id,
)?;
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(package);
return Ok(T::from_relationship_id(relationship_id, part_id));
}
let (imported_part_id, added_count) = crate::sdk::SdkPackage::storage_mut(package)
.import_part_tree_from(
crate::sdk::SdkPackage::storage(source_package),
part.part_id(),
Some(self.part_id()),
relationship_id.clone(),
|part_id, _| crate::sdk::SdkPackage::part_bytes_for_copy(source_package, part_id),
)?;
for _ in 0..added_count {
crate::sdk::SdkPackage::push_root_element_slot(package);
}
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(package);
Ok(T::from_relationship_id(relationship_id, imported_part_id))
}
#[inline]
fn create_relationship_to_part<P: SdkPackage, T: SdkPart>(
&self,
package: &mut P,
part: T,
) -> Result<String, crate::common::SdkError> {
if let Some(relationship_id) = self.get_id_of_part(package, &part) {
return Ok(relationship_id.to_string());
}
let relationship_id = self.next_relationship_id(package)?;
self.create_relationship_to_part_with_id(package, part, relationship_id)
}
#[inline]
fn create_relationship_to_part_with_id<P: SdkPackage, T: SdkPart>(
&self,
package: &mut P,
part: T,
relationship_id: impl Into<String>,
) -> Result<String, crate::common::SdkError> {
let relationship_id = relationship_id.into();
crate::sdk::SdkPackage::storage_mut(package).add_child_relationship_to_part(
self.part_id(),
relationship_id.clone(),
part.part_id(),
)?;
crate::sdk::SdkPackage::refresh_relationship_model_from_storage(package);
Ok(relationship_id)
}
}
#[cfg(feature = "parts")]
pub trait SdkDataPartReference: Sized {
const RELATIONSHIP_TYPE: &'static str;
fn new_from_archive<R: std::io::Read + std::io::Seek>(
path: &str,
r_id: &str,
part_index: usize,
archive: &mut zip::ZipArchive<R>,
) -> Result<Self, crate::common::SdkError>;
fn save_zip<W: std::io::Write + std::io::Seek>(
&self,
parent_path: &str,
zip: &mut zip::ZipWriter<W>,
entry_set: &mut std::collections::HashSet<String>,
) -> Result<(), crate::common::SdkError>;
}