use crate::deserializer::iter::{Property, PropertyIterator};
use crate::models::output_data::OutputData;
impl<'a, 'b: 'a> Property<'a, 'b> {
#[must_use]
pub fn as_i64(&self) -> Option<i64> {
match self.scalar()? {
OutputData::SignedInteger(v) => Some(*v),
OutputData::UnsignedInteger(v) => i64::try_from(*v).ok(),
_ => None,
}
}
#[must_use]
pub fn as_u64(&self) -> Option<u64> {
match self.scalar()? {
OutputData::UnsignedInteger(v) => Some(*v),
OutputData::SignedInteger(v) => u64::try_from(*v).ok(),
_ => None,
}
}
#[must_use]
pub fn as_f64(&self) -> Option<f64> {
match self.scalar()? {
OutputData::Double(v) => Some(*v),
OutputData::Float(v) => Some(f64::from(*v)),
_ => None,
}
}
fn scalar(&self) -> Option<&'b OutputData<'a>> {
match self {
Property::Primitive(value) => Some(value),
Property::Object {
name: "NSNumber",
data,
..
} => number_value(data.clone()),
Property::Group(group) => match group.first()? {
Property::Primitive(value) => Some(value),
Property::Object {
name: "NSNumber",
data,
..
} => number_value(data),
_ => None,
},
_ => None,
}
}
}
fn number_value<'a, 'b: 'a>(mut data: PropertyIterator<'a, 'b>) -> Option<&'b OutputData<'a>> {
match data.next()? {
Property::Group(inner) => match inner.first()? {
Property::Primitive(value) => Some(value),
_ => None,
},
_ => None,
}
}
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use crate::deserializer::foundation::test_support::load;
use crate::deserializer::typedstream::TypedStreamDeserializer;
#[test]
fn root_object_resolves_as_i64() {
let bytes = load("foundation/NumberInt");
let mut ts = TypedStreamDeserializer::new(&bytes);
assert_eq!(ts.root().unwrap().as_i64(), Some(42));
}
#[test]
fn as_f64_reads_decimal_double() {
let bytes = load("foundation/NumberDouble");
let mut ts = TypedStreamDeserializer::new(&bytes);
let root = ts.oxidize().unwrap();
let group = ts.resolve_properties(root).unwrap().next().unwrap();
assert_eq!(group.as_f64(), Some(100.5));
}
#[test]
fn as_f64_reads_float() {
let bytes = load("foundation/NumberFloat");
let mut ts = TypedStreamDeserializer::new(&bytes);
let root = ts.oxidize().unwrap();
let group = ts.resolve_properties(root).unwrap().next().unwrap();
assert_eq!(group.as_f64(), Some(3.5));
}
#[test]
fn as_i64_reads_large_negative() {
let bytes = load("foundation/NumberInt64");
let mut ts = TypedStreamDeserializer::new(&bytes);
let root = ts.oxidize().unwrap();
let group = ts.resolve_properties(root).unwrap().next().unwrap();
assert_eq!(group.as_i64(), Some(-9_000_000_000));
}
#[test]
fn integer_coercion_and_strictness() {
let bytes = load("foundation/NumberInt");
let mut ts = TypedStreamDeserializer::new(&bytes);
let root = ts.oxidize().unwrap();
let group = ts.resolve_properties(root).unwrap().next().unwrap();
assert_eq!(group.as_i64(), Some(42));
assert_eq!(group.as_u64(), Some(42)); assert_eq!(group.as_f64(), None); assert_eq!(group.as_string(), None); assert_eq!(group.as_data(), None);
}
#[test]
fn unsigned_coercion_rejects_negative() {
let bytes = load("foundation/NumberInt64"); let mut ts = TypedStreamDeserializer::new(&bytes);
let root = ts.oxidize().unwrap();
let group = ts.resolve_properties(root).unwrap().next().unwrap();
assert_eq!(group.as_u64(), None);
}
#[test]
fn unwraps_nsnumber_object_and_reads_dict_entries() {
let bytes = load("foundation/NSDictionaryNested");
let mut ts = TypedStreamDeserializer::new(&bytes);
let root = ts.oxidize().unwrap();
let keys: Vec<&str> = ts
.resolve_properties(root)
.unwrap()
.filter_map(|group| group.as_string())
.collect();
assert!(
keys.contains(&"arr") && keys.contains(&"data") && keys.contains(&"num"),
"{keys:?}"
);
let mut ts = TypedStreamDeserializer::new(&bytes);
let root = ts.oxidize().unwrap();
let ints: Vec<i64> = ts
.resolve_properties(root)
.unwrap()
.filter_map(|group| group.as_i64())
.collect();
assert!(ints.contains(&7), "{ints:?}");
let mut ts = TypedStreamDeserializer::new(&bytes);
let root = ts.oxidize().unwrap();
let datas: Vec<&[u8]> = ts
.resolve_properties(root)
.unwrap()
.filter_map(|group| group.as_data())
.collect();
assert!(datas.contains(&&[0x01, 0x02][..]), "{datas:?}"); }
}