use std::{collections::HashMap, io};
use ltk_io_ext::{measure, window_at};
use super::super::{BinProperty, Error, PropertyValueEnum};
use byteorder::{ReadBytesExt, WriteBytesExt, LE};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct BinTreeObject {
pub path_hash: u32,
pub class_hash: u32,
pub properties: HashMap<u32, BinProperty>,
}
impl BinTreeObject {
pub fn new(path_hash: u32, class_hash: u32) -> Self {
Self {
path_hash,
class_hash,
properties: HashMap::new(),
}
}
pub fn builder(path_hash: u32, class_hash: u32) -> BinTreeObjectBuilder {
BinTreeObjectBuilder::new(path_hash, class_hash)
}
pub fn from_reader<R: io::Read + io::Seek + ?Sized>(
reader: &mut R,
class_hash: u32,
legacy: bool,
) -> Result<Self, Error> {
let size = reader.read_u32::<LE>()?;
let (real_size, value) = measure(reader, |reader| {
let path_hash = reader.read_u32::<LE>()?;
let prop_count = reader.read_u16::<LE>()? as usize;
let mut properties = HashMap::with_capacity(prop_count);
for _ in 0..prop_count {
let prop = BinProperty::from_reader(reader, legacy)?;
properties.insert(prop.name_hash, prop);
}
Ok::<_, Error>(Self {
path_hash,
class_hash,
properties,
})
})?;
if size as u64 != real_size {
return Err(Error::InvalidSize(size as _, real_size));
}
Ok(value)
}
pub fn to_writer<W: io::Write + io::Seek + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
let size_pos = writer.stream_position()?;
writer.write_u32::<LE>(0)?;
let (size, _) = measure(writer, |writer| {
writer.write_u32::<LE>(self.path_hash)?;
writer.write_u16::<LE>(self.properties.len() as _)?;
for prop in self.properties.values() {
prop.to_writer(writer)?;
}
Ok::<_, io::Error>(())
})?;
window_at(writer, size_pos, |writer| writer.write_u32::<LE>(size as _))?;
Ok(())
}
#[inline]
pub fn len(&self) -> usize {
self.properties.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.properties.is_empty()
}
#[inline]
pub fn get_property(&self, name_hash: u32) -> Option<&BinProperty> {
self.properties.get(&name_hash)
}
#[inline]
pub fn get_property_mut(&mut self, name_hash: u32) -> Option<&mut BinProperty> {
self.properties.get_mut(&name_hash)
}
#[inline]
pub fn get_value(&self, name_hash: u32) -> Option<&PropertyValueEnum> {
self.properties.get(&name_hash).map(|p| &p.value)
}
#[inline]
pub fn get_value_mut(&mut self, name_hash: u32) -> Option<&mut PropertyValueEnum> {
self.properties.get_mut(&name_hash).map(|p| &mut p.value)
}
#[inline]
pub fn contains_property(&self, name_hash: u32) -> bool {
self.properties.contains_key(&name_hash)
}
pub fn set_property(&mut self, property: BinProperty) -> Option<BinProperty> {
self.properties.insert(property.name_hash, property)
}
pub fn set_value(
&mut self,
name_hash: u32,
value: impl Into<PropertyValueEnum>,
) -> Option<BinProperty> {
self.properties.insert(
name_hash,
BinProperty {
name_hash,
value: value.into(),
},
)
}
pub fn remove_property(&mut self, name_hash: u32) -> Option<BinProperty> {
self.properties.remove(&name_hash)
}
#[inline]
pub fn iter(&self) -> impl Iterator<Item = (&u32, &BinProperty)> {
self.properties.iter()
}
#[inline]
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&u32, &mut BinProperty)> {
self.properties.iter_mut()
}
}
impl<'a> IntoIterator for &'a BinTreeObject {
type Item = (&'a u32, &'a BinProperty);
type IntoIter = std::collections::hash_map::Iter<'a, u32, BinProperty>;
fn into_iter(self) -> Self::IntoIter {
self.properties.iter()
}
}
impl<'a> IntoIterator for &'a mut BinTreeObject {
type Item = (&'a u32, &'a mut BinProperty);
type IntoIter = std::collections::hash_map::IterMut<'a, u32, BinProperty>;
fn into_iter(self) -> Self::IntoIter {
self.properties.iter_mut()
}
}
impl IntoIterator for BinTreeObject {
type Item = (u32, BinProperty);
type IntoIter = std::collections::hash_map::IntoIter<u32, BinProperty>;
fn into_iter(self) -> Self::IntoIter {
self.properties.into_iter()
}
}
#[derive(Debug, Clone)]
pub struct BinTreeObjectBuilder {
path_hash: u32,
class_hash: u32,
properties: HashMap<u32, BinProperty>,
}
impl BinTreeObjectBuilder {
pub fn new(path_hash: u32, class_hash: u32) -> Self {
Self {
path_hash,
class_hash,
properties: HashMap::new(),
}
}
pub fn path_hash(mut self, path_hash: u32) -> Self {
self.path_hash = path_hash;
self
}
pub fn class_hash(mut self, class_hash: u32) -> Self {
self.class_hash = class_hash;
self
}
pub fn bin_property(mut self, prop: BinProperty) -> Self {
self.properties.insert(prop.name_hash, prop);
self
}
pub fn property(mut self, name_hash: u32, value: impl Into<PropertyValueEnum>) -> Self {
self.properties.insert(
name_hash,
BinProperty {
name_hash,
value: value.into(),
},
);
self
}
pub fn bin_properties(mut self, props: impl IntoIterator<Item = BinProperty>) -> Self {
for prop in props {
self.properties.insert(prop.name_hash, prop);
}
self
}
pub fn build(self) -> BinTreeObject {
BinTreeObject {
path_hash: self.path_hash,
class_hash: self.class_hash,
properties: self.properties,
}
}
}