use core::slice::Iter;
use alloc::vec::Vec;
use crate::models::{
archived::{Archived, ObjectData},
class::Class,
output_data::OutputData,
types::{Type, TypeEntry},
};
#[derive(Debug)]
pub enum Property<'a, 'b> {
Object {
class: &'a Class,
name: &'a str,
data: PropertyIterator<'a, 'b>,
},
Group(PropertyGroup<'a, 'b>),
Primitive(&'b OutputData<'a>),
}
#[derive(Debug, Clone, Copy)]
pub struct PropertyGroup<'a, 'b> {
items: &'b [OutputData<'a>],
object_table: &'b [Archived<'a>],
type_table: &'b [TypeEntry<'a>],
}
impl<'a, 'b: 'a> PropertyGroup<'a, 'b> {
#[must_use]
pub fn len(&self) -> usize {
self.items.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
#[must_use]
pub fn get(&self, index: usize) -> Option<Property<'a, 'b>> {
let item = self.items.get(index)?;
Some(self.resolve(item))
}
#[must_use]
pub fn first(&self) -> Option<Property<'a, 'b>> {
self.get(0)
}
#[must_use]
pub fn iter(&self) -> PropertyGroupIter<'a, 'b> {
PropertyGroupIter {
group: *self,
front: 0,
back: self.items.len(),
}
}
fn resolve(&self, item: &'b OutputData<'a>) -> Property<'a, 'b> {
if let OutputData::Object(idx) = item
&& let Some(Archived::Object { class: cls, .. }) = self.object_table.get(*idx)
&& let Some(Archived::Class(cls)) = self.object_table.get(*cls)
&& let Some(sub_iter) = PropertyIterator::new(self.object_table, self.type_table, *idx)
{
let class_name = self
.type_table
.get(cls.name_index)
.and_then(|types| types.first())
.and_then(|t| match t {
Type::String(name) => Some(*name),
_ => None,
})
.unwrap_or("Unknown Class");
return Property::Object {
class: cls,
name: class_name,
data: sub_iter,
};
}
Property::Primitive(item)
}
}
impl<'a, 'b: 'a> IntoIterator for PropertyGroup<'a, 'b> {
type Item = Property<'a, 'b>;
type IntoIter = PropertyGroupIter<'a, 'b>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, 'b: 'a> IntoIterator for &PropertyGroup<'a, 'b> {
type Item = Property<'a, 'b>;
type IntoIter = PropertyGroupIter<'a, 'b>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[derive(Debug, Clone)]
pub struct PropertyGroupIter<'a, 'b> {
group: PropertyGroup<'a, 'b>,
front: usize,
back: usize,
}
impl<'a, 'b: 'a> Iterator for PropertyGroupIter<'a, 'b> {
type Item = Property<'a, 'b>;
fn next(&mut self) -> Option<Self::Item> {
if self.front >= self.back {
return None;
}
let item = self.group.get(self.front);
self.front += 1;
item
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.back - self.front;
(remaining, Some(remaining))
}
}
impl<'a, 'b: 'a> DoubleEndedIterator for PropertyGroupIter<'a, 'b> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.front >= self.back {
return None;
}
self.back -= 1;
self.group.get(self.back)
}
}
impl<'a, 'b: 'a> ExactSizeIterator for PropertyGroupIter<'a, 'b> {}
#[derive(Debug, Clone)]
pub struct PropertyIterator<'a, 'b> {
object_table: &'b [Archived<'a>],
type_table: &'b [TypeEntry<'a>],
groups: GroupSource<'a, 'b>,
}
#[derive(Debug, Clone)]
enum GroupSource<'a, 'b> {
Inline(Option<&'b OutputData<'a>>),
Groups(Iter<'b, Vec<OutputData<'a>>>),
}
impl<'a, 'b> PropertyIterator<'a, 'b> {
pub(crate) fn new(
object_table: &'b [Archived<'a>],
type_table: &'b [TypeEntry<'a>],
root_object_index: usize,
) -> Option<Self> {
let root_object = object_table.get(root_object_index)?;
let Archived::Object { data, .. } = root_object else {
return None;
};
let groups = match data {
ObjectData::Empty => GroupSource::Inline(None),
ObjectData::Inline(value) => GroupSource::Inline(Some(value)),
ObjectData::Groups(groups) => GroupSource::Groups(groups.iter()),
};
Some(Self {
object_table,
type_table,
groups,
})
}
}
impl<'a, 'b: 'a> PropertyIterator<'a, 'b> {
#[must_use]
pub fn primitives(self) -> Vec<&'b OutputData<'a>> {
self.primitives_with_limits(100, 1_000_000)
}
#[must_use]
pub fn primitives_with_limits(
self,
max_depth: usize,
max_items: usize,
) -> Vec<&'b OutputData<'a>> {
let mut primitives = Vec::new();
let mut processed_items = 0;
let initial_props: Vec<Property<'a, 'b>> = self.collect();
let mut stack: Vec<(Property<'a, 'b>, usize)> =
initial_props.into_iter().map(|p| (p, 0)).collect();
while let Some((prop, depth)) = stack.pop() {
if processed_items >= max_items {
break;
}
if depth >= max_depth {
continue;
}
processed_items += 1;
match prop {
Property::Primitive(p) => primitives.push(p),
Property::Group(group) => {
for child in group.into_iter().rev() {
stack.push((child, depth + 1));
}
}
Property::Object { data, .. } => {
let mut nested: Vec<_> = data.collect();
while let Some(child) = nested.pop() {
stack.push((child, depth + 1));
}
}
}
}
primitives.reverse();
primitives
}
}
impl<'a, 'b: 'a> Iterator for PropertyIterator<'a, 'b> {
type Item = Property<'a, 'b>;
fn next(&mut self) -> Option<Self::Item> {
let items: &'b [OutputData<'a>] = match &mut self.groups {
GroupSource::Inline(slot) => core::slice::from_ref(slot.take()?),
GroupSource::Groups(iter) => iter.next()?.as_slice(),
};
Some(Property::Group(PropertyGroup {
items,
object_table: self.object_table,
type_table: self.type_table,
}))
}
}
#[cfg(any(feature = "std", test))]
pub fn print_resolved(iter: PropertyIterator<'_, '_>, indent: usize) {
print_resolved_with_limits(iter, indent, 100, 1_000_000);
}
#[cfg(any(feature = "std", test))]
fn print_resolved_with_limits(
iter: PropertyIterator<'_, '_>,
indent: usize,
max_depth: usize,
max_items: usize,
) {
extern crate std;
use std::println;
let mut stack: Vec<(Property<'_, '_>, usize)> = Vec::new();
let mut items_printed = 0;
for prop in iter {
stack.push((prop, indent));
}
while let Some((prop, current_indent)) = stack.pop() {
if items_printed >= max_items {
println!(
"{:indent$}... (truncated after {max_items} items)",
"",
indent = current_indent
);
break;
}
let depth = (current_indent - indent) / 2;
if depth >= max_depth {
println!(
"{:indent$}... (max depth {max_depth} reached)",
"",
indent = current_indent
);
continue;
}
items_printed += 1;
match prop {
Property::Object {
class: _,
name,
data,
} => {
println!("{:indent$}Object: {:?}", "", name, indent = current_indent);
let children: Vec<_> = data.collect();
for child in children.into_iter().rev() {
stack.push((child, current_indent + 2));
}
}
Property::Group(group) => {
println!("{:indent$}Group:", "", indent = current_indent);
for slot in group.into_iter().rev() {
stack.push((slot, current_indent + 2));
}
}
Property::Primitive(p) => {
println!("{:indent$}Primitive: {:?}", "", p, indent = current_indent);
}
}
}
}