use std::io::{Read, Seek}; use serde::de::Error as _; use serde::Deserialize; use serde_json::{Deserializer, StreamDeserializer}; use serde_json::de::IoRead; pub use zip::{read::ZipFile, ZipArchive}; use crate::error::{Error, Result}; use crate::model::{ModPack, SimpleMod}; use crate::model::manifest_kind::ManifestKind; pub mod model; pub mod error; pub(crate) mod tracking_reader; pub mod ttmp_extractor; pub fn from_value(value: serde_json::Value) -> Result { let manifest = if value.is_array() { ManifestKind::V1(serde_json::from_value(value).map_err(Error::InvalidManifest)?) } else { ManifestKind::V2(serde_json::from_value(value).map_err(Error::InvalidManifest)?) }; Ok(manifest) } pub fn from_reader(reader: R) -> Result<(ManifestKind, ZipArchive)> { let mut zip = ZipArchive::new(reader).map_err(Error::Zip)?; let manifest = zip.by_name("TTMPL.mpl").map_err(Error::Zip)?; let mut manifest = StreamDeserializer::new(IoRead::new(manifest)) .collect::, _>>() .map_err(Error::InvalidManifest)?; #[derive(Deserialize)] #[serde(untagged)] enum DesManifest { V1(SimpleMod), V2(ModPack), } let all_v1 = manifest.iter().all(|inner| matches!(inner, DesManifest::V1(_))); let transformed = if all_v1 { let mods: Vec = manifest.into_iter() .map(|inner| match inner { DesManifest::V1(inner) => inner, _ => unreachable!(), }) .collect(); ManifestKind::V1(mods) } else if manifest.len() == 1 && matches!(&manifest[0], DesManifest::V2(_)) { ManifestKind::V2(match manifest.remove(0) { DesManifest::V2(pack) => pack, _ => unreachable!(), }) } else { return Err(Error::InvalidManifest(serde_json::Error::custom("unrecognised manifest"))); }; Ok((transformed, zip)) }