use {
crate::{
component_package::ComponentPackageReader, distribution::Distribution, Error, PkgResult,
},
apple_xar::reader::XarReader,
std::{
fmt::Debug,
io::{Cursor, Read, Seek},
},
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PkgFlavor {
Component,
Product,
}
pub struct PkgReader<R: Read + Seek + Sized + Debug> {
xar: XarReader<R>,
flavor: PkgFlavor,
}
impl<R: Read + Seek + Sized + Debug> PkgReader<R> {
pub fn new(reader: R) -> PkgResult<Self> {
let xar = XarReader::new(reader)?;
let flavor = if xar.find_file("Distribution")?.is_some() {
PkgFlavor::Product
} else {
PkgFlavor::Component
};
Ok(Self { xar, flavor })
}
pub fn into_inner(self) -> XarReader<R> {
self.xar
}
pub fn flavor(&self) -> PkgFlavor {
self.flavor
}
pub fn distribution(&mut self) -> PkgResult<Option<Distribution>> {
if let Some(xml_data) = self.xar.get_file_data_from_path("Distribution")? {
Ok(Some(Distribution::from_reader(Cursor::new(xml_data))?))
} else {
Ok(None)
}
}
pub fn resolve_component(
&mut self,
path_prefix: &str,
) -> PkgResult<Option<ComponentPackageReader>> {
let prefix = if path_prefix.is_empty() {
"".to_string()
} else {
format!("{path_prefix}/")
};
let mut bom_data = None;
let mut package_info_data = None;
let mut payload_data = None;
let mut scripts_data = None;
for (filename, file) in self
.xar
.files()?
.into_iter()
.filter(|(filename, _)| filename.starts_with(&prefix))
{
let mut data = Vec::<u8>::with_capacity(file.size.unwrap_or(0) as _);
self.xar
.write_file_data_decoded_from_file(&file, &mut data)?;
let filename = filename.strip_prefix(&prefix).expect("prefix should match");
match filename {
"Bom" => {
bom_data = Some(data);
}
"PackageInfo" => {
package_info_data = Some(data);
}
"Payload" => {
payload_data = Some(data);
}
"Scripts" => {
scripts_data = Some(data);
}
_ => {}
}
}
if bom_data.is_some()
|| package_info_data.is_some()
|| payload_data.is_some()
|| scripts_data.is_some()
{
Ok(Some(ComponentPackageReader::from_file_data(
bom_data,
package_info_data,
payload_data,
scripts_data,
)?))
} else {
Ok(None)
}
}
pub fn root_component(&mut self) -> PkgResult<Option<ComponentPackageReader>> {
self.resolve_component("")
}
pub fn component_packages(&mut self) -> PkgResult<Vec<ComponentPackageReader>> {
let components = self
.xar
.files()?
.into_iter()
.filter_map(|(filename, _)| {
if filename.ends_with(".pkg") && !filename.contains('/') {
Some(filename)
} else {
None
}
})
.collect::<Vec<_>>();
let mut res = vec![];
for component in components {
res.push(
self.resolve_component(&component)?
.ok_or(Error::ComponentResolution)?,
);
}
Ok(res)
}
}