use crate::deserializer::foundation::names::{ATTRIBUTED_STRING_CLASSES, STRING_CLASSES};
use crate::deserializer::iter::{Property, PropertyIterator};
use crate::models::output_data::OutputData;
fn backing_string<'a, 'b: 'a>(mut data: PropertyIterator<'a, 'b>) -> Option<&'a str> {
if let Property::Group(group) = data.next()?
&& let Some(Property::Primitive(OutputData::String(s))) = group.first()
{
return Some(s);
}
None
}
impl<'a, 'b: 'a> Property<'a, 'b> {
#[must_use]
pub fn as_string(&self) -> Option<&'a str> {
if let Some(data) = self.object_in_classes(STRING_CLASSES) {
return backing_string(data);
}
if let Some(data) = self.object_in_classes(ATTRIBUTED_STRING_CLASSES) {
for prop in data {
if let Property::Group(group) = prop
&& let Some(Property::Object {
name, data: inner, ..
}) = group.first()
&& STRING_CLASSES.contains(&name)
{
return backing_string(inner);
}
}
}
None
}
}
#[cfg(test)]
mod tests {
use alloc::{vec, vec::Vec};
use crate::deserializer::foundation::test_support::load;
use crate::deserializer::typedstream::TypedStreamDeserializer;
#[test]
fn root_object_resolves_as_string() {
let bytes = load("foundation/NSString");
let mut ts = TypedStreamDeserializer::new(&bytes);
assert_eq!(ts.root().unwrap().as_string(), Some("Hello, world"));
}
#[test]
fn as_string_reads_both_string_variants() {
let bytes = load("foundation/NestedStrings");
let mut ts = TypedStreamDeserializer::new(&bytes);
let root = ts.oxidize().unwrap();
let strings: Vec<&str> = ts
.resolve_properties(root)
.unwrap()
.filter_map(|group| group.as_string())
.collect();
assert_eq!(strings.len(), 2, "{strings:?}");
assert!(strings.contains(&"imm"), "{strings:?}");
assert!(strings.contains(&"mut"), "{strings:?}");
}
#[test]
fn as_string_reads_attributed_backing_text() {
let bytes = load("foundation/NestedAttributed");
let mut ts = TypedStreamDeserializer::new(&bytes);
let root = ts.oxidize().unwrap();
let strings: Vec<&str> = ts
.resolve_properties(root)
.unwrap()
.filter_map(|group| group.as_string())
.collect();
assert_eq!(strings, vec!["styled"]);
}
#[test]
fn as_string_reads_real_mutable_attributed_body() {
let bytes = load("AttributedBodyTextOnly");
let mut ts = TypedStreamDeserializer::new(&bytes);
let root = ts.oxidize().unwrap();
let strings: Vec<&str> = ts
.resolve_properties(root)
.unwrap()
.filter_map(|group| group.as_string())
.collect();
assert!(strings.contains(&"Noter test"), "{strings:?}");
}
}