use serde::Deserialize;
use super::container;
use super::{GeneratorRef, Limits, Meta, Metadata, PortableError, RendererRef};
#[derive(Debug, Deserialize)]
struct Header {
schema: u32,
generator: GeneratorRef,
renderer: RendererRef,
#[serde(default)]
meta: Option<Meta>,
}
pub fn inspect(bytes: &[u8], limits: &Limits) -> Result<Metadata, PortableError> {
let dir = container::directory(bytes, limits)?;
let json = container::chunk(bytes, &dir, container::TAG_JSON).expect("directory checked JSON");
if json.len() > limits.max_json_bytes {
return Err(PortableError::Budget(format!(
"JSON chunk is {} bytes, over the {} byte limit",
json.len(),
limits.max_json_bytes
)));
}
let header: Header = serde_json::from_slice(json)?;
if let Some(meta) = &header.meta {
if let Some(poster) = &meta.poster {
if poster.bytes > limits.max_poster_bytes {
return Err(PortableError::Budget(format!(
"poster is {} bytes, over the {} byte limit",
poster.bytes, limits.max_poster_bytes
)));
}
let actual = container::chunk(bytes, &dir, container::TAG_PSTR).map(<[u8]>::len);
if actual != Some(poster.bytes) {
return Err(PortableError::Malformed(format!(
"spec declares a {}-byte poster but the PSTR chunk holds {}",
poster.bytes,
actual.map_or_else(|| "nothing".to_string(), |n| n.to_string())
)));
}
}
let pixels = (meta.width_px as usize).saturating_mul(meta.height_px as usize);
if pixels > limits.max_canvas_pixels {
return Err(PortableError::Budget(format!(
"figure is {}x{} = {pixels} pixels, over the {} pixel limit",
meta.width_px, meta.height_px, limits.max_canvas_pixels
)));
}
}
Ok(Metadata {
schema: header.schema,
schema_supported: (super::SCHEMA_MIN..=super::SCHEMA_VERSION).contains(&header.schema),
generator: header.generator,
renderer: header.renderer,
meta: header.meta,
chunks: dir,
total_bytes: bytes.len(),
})
}