mod value;
pub use value::{
ContainerItems, ContainerView, MapEntries, MapView, OptionalView, StructView, ValueView,
};
use std::{fmt, marker::PhantomData};
use ltk_hash::BinHash;
use crate::{
path::ValueShape,
property::{Kind, NoMeta},
stream::{
layout::{Cursor, Numbering},
owned, ObjectEntry,
},
Error, PropertyValueEnum,
};
const OBJECT_HEADER: usize = 4 + 4 + 2;
pub struct ObjectView<'a, M = NoMeta> {
path_hash: BinHash,
class_hash: BinHash,
raw: &'a [u8],
properties: Cursor<'a>,
property_count: u16,
meta: PhantomData<fn() -> M>,
}
impl<'a, M> ObjectView<'a, M> {
pub(crate) fn new(entry: ObjectEntry, mut cur: Cursor<'a>) -> Result<Self, Error> {
let raw = cur.rest();
cur.skip(OBJECT_HEADER - 2)?;
let property_count = cur.u16()?;
Ok(Self {
path_hash: entry.path_hash,
class_hash: entry.class_hash,
raw,
properties: cur,
property_count,
meta: PhantomData,
})
}
#[must_use]
pub fn path_hash(&self) -> BinHash {
self.path_hash
}
#[must_use]
pub fn class_hash(&self) -> BinHash {
self.class_hash
}
#[must_use]
pub fn property_count(&self) -> u16 {
self.property_count
}
#[must_use]
pub fn numbering(&self) -> Numbering {
self.properties.numbering()
}
pub fn properties(&self) -> Properties<'a, M> {
Properties::new(self.properties, self.property_count)
}
pub fn property(
&self,
name_hash: impl Into<BinHash>,
) -> Result<Option<PropertyView<'a, M>>, Error> {
find_property(self.properties(), name_hash.into())
}
#[must_use]
pub fn raw(&self) -> &'a [u8] {
self.raw
}
}
impl<M> fmt::Debug for ObjectView<'_, M> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ObjectView")
.field("path_hash", &self.path_hash)
.field("class_hash", &self.class_hash)
.field("property_count", &self.property_count)
.field("bytes", &self.raw.len())
.field("numbering", &self.numbering())
.finish()
}
}
pub struct PropertyView<'a, M = NoMeta> {
name_hash: BinHash,
kind: Kind,
value: Cursor<'a>,
meta: PhantomData<fn() -> M>,
}
impl<'a, M> PropertyView<'a, M> {
#[must_use]
pub fn name_hash(&self) -> BinHash {
self.name_hash
}
#[must_use]
pub fn kind(&self) -> Kind {
self.kind
}
#[must_use]
pub fn raw(&self) -> &'a [u8] {
self.value.rest()
}
pub fn shape(&self) -> Result<ValueShape, Error> {
self.value.value_shape(self.kind)
}
pub fn item_count(&self) -> Result<Option<u32>, Error> {
let mut cur = self.value;
let count = match self.kind {
Kind::Container | Kind::UnorderedContainer => {
cur.skip(1 + 4)?;
cur.u32()?
}
Kind::Map => {
cur.skip(2 + 4)?;
cur.u32()?
}
_ => return Ok(None),
};
Ok(Some(count))
}
pub fn value_view(&self) -> Result<ValueView<'a, M>, Error> {
ValueView::read(&mut { self.value }, self.kind)
}
}
impl<M: Default> PropertyView<'_, M> {
pub fn value(&self) -> Result<PropertyValueEnum<M>, Error> {
owned::read_value(&mut { self.value }, self.kind)
}
}
impl<M> fmt::Debug for PropertyView<'_, M> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PropertyView")
.field("name_hash", &self.name_hash)
.field("kind", &self.kind)
.field("bytes", &self.value.remaining())
.finish()
}
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct Properties<'a, M = NoMeta> {
cur: Cursor<'a>,
remaining: u16,
meta: PhantomData<fn() -> M>,
}
impl<'a, M> Properties<'a, M> {
pub(crate) fn new(cur: Cursor<'a>, count: u16) -> Self {
Self {
cur,
remaining: count,
meta: PhantomData,
}
}
}
impl<'a, M> Iterator for Properties<'a, M> {
type Item = Result<PropertyView<'a, M>, Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
self.remaining -= 1;
Some(match read_property(&mut self.cur) {
Ok(property) => Ok(property),
Err(error) => {
self.remaining = 0;
Err(error)
}
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.remaining as usize, Some(self.remaining as usize))
}
}
impl<M> std::iter::FusedIterator for Properties<'_, M> {}
impl<M> Clone for Properties<'_, M> {
fn clone(&self) -> Self {
Self {
cur: self.cur,
remaining: self.remaining,
meta: PhantomData,
}
}
}
impl<M> fmt::Debug for Properties<'_, M> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Properties")
.field("remaining", &self.remaining)
.finish()
}
}
fn read_property<'a, M>(cur: &mut Cursor<'a>) -> Result<PropertyView<'a, M>, Error> {
let name_hash = cur.bin_hash()?;
let kind = cur.kind()?;
let value = cur.take_value(kind)?;
Ok(PropertyView {
name_hash,
kind,
value: Cursor::new(value, cur.numbering()),
meta: PhantomData,
})
}
pub(crate) fn find_property<'a, M>(
properties: Properties<'a, M>,
name_hash: BinHash,
) -> Result<Option<PropertyView<'a, M>>, Error> {
for property in properties {
let property = property?;
if property.name_hash == name_hash {
return Ok(Some(property));
}
}
Ok(None)
}
macro_rules! copy_views {
($($view:ident),* $(,)?) => { $(
impl<M> Clone for $view<'_, M> {
fn clone(&self) -> Self {
*self
}
}
impl<M> Copy for $view<'_, M> {}
)* };
}
pub(crate) use copy_views;
copy_views!(ObjectView, PropertyView);