use crate::Result;
use crate::hash::fnv1a64;
use crate::read::Bytes;
#[derive(Debug, Clone, PartialEq)]
pub enum PropertyValue {
Bool(bool),
Int(i64),
Float(f64),
Text(String),
}
#[derive(Debug, Clone, PartialEq)]
pub struct Property {
pub key_hash: u64,
pub key: String,
pub value: PropertyValue,
}
pub fn property<'a>(properties: &'a [Property], key: &str) -> Option<&'a PropertyValue> {
let hash = fnv1a64(key.as_bytes());
properties
.iter()
.find(|p| p.key_hash == hash && p.key == key)
.map(|p| &p.value)
}
pub(crate) fn properties_of(chunk: Bytes<'_>, strings: Bytes<'_>) -> Result<Vec<Property>> {
chunk
.records(32)?
.map(|p| {
let value = match p.u32(16)? {
0 => PropertyValue::Bool(p.u64(24)? != 0),
1 => PropertyValue::Int(p.i64(24)?),
2 => PropertyValue::Float(p.f64(24)?),
3 => PropertyValue::Text(p.text_ref(24, strings)?),
t => return Err(p.err(format!("unknown property type {t}"))),
};
Ok(Property {
key_hash: p.u64(0)?,
key: p.text_ref(8, strings)?,
value,
})
})
.collect()
}
pub(crate) fn read_properties(
props: Option<Bytes<'_>>,
strings: Bytes<'_>,
first: u32,
count: u32,
) -> Result<Vec<Property>> {
if count == 0 {
return Ok(Vec::new());
}
let props = props.ok_or_else(|| strings.err("properties without a PROPS chunk"))?;
let at = (first as usize)
.checked_mul(32)
.ok_or_else(|| props.err("property index overflows"))?;
let len = (count as usize)
.checked_mul(32)
.ok_or_else(|| props.err("property count overflows"))?;
properties_of(Bytes::new(props.slice(at, len)?, props.file), strings)
}