use crate::{Ecc, Endian, Error, Result, BE, LE, NE};
use byteorder::{ByteOrder, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Write};
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct Hierarchical {
key: String,
content: Vec<String>,
children: Vec<Hierarchical>,
}
impl Hierarchical {
const ID: Ecc = Ecc::new("STR_HIER");
pub fn new<T: Into<String>>(key: T, content: Vec<String>, children: Vec<Self>) -> Self {
Self {
key: key.into(),
content,
children,
}
}
pub fn key(&self) -> &str {
&self.key
}
pub fn key_mut(&mut self) -> &mut String {
&mut self.key
}
pub fn content(&self) -> &[String] {
&self.content
}
pub fn content_mut(&mut self) -> &mut Vec<String> {
&mut self.content
}
pub fn push<T: Into<String>>(&mut self, item: T) {
self.content.push(item.into());
}
pub fn children(&self) -> &[Self] {
&self.children
}
pub fn children_mut(&mut self) -> &mut Vec<Self> {
&mut self.children
}
pub fn push_child(&mut self, item: Self) {
self.children.push(item);
}
pub fn to_bytes<E: ByteOrder>(self) -> Result<Vec<u8>> {
let mut bytes = vec![];
let writer: &mut dyn Write = &mut bytes;
Self::ID.write::<E>(writer)?;
self.write_structure::<E>(writer)?;
Ok(bytes)
}
fn write_structure<E: ByteOrder>(self, writer: &mut dyn Write) -> Result<()> {
Self::write_string::<E>(&self.key, writer)?;
writer.write_u32::<E>(self.content.len() as u32)?;
for s in self.content {
Self::write_string::<E>(&s, writer)?;
}
writer.write_u32::<E>(self.children.len() as u32)?;
for c in self.children {
c.write_structure::<E>(writer)?;
}
Ok(())
}
fn write_string<E: ByteOrder>(value: &str, writer: &mut dyn Write) -> Result<()> {
if value.len() > core::u16::MAX as usize {
return Err(Error::Invalid("String length greater than max u16!".into()));
}
writer.write_u16::<E>(value.len() as u16)?;
writer.write_all(value.as_bytes())?;
Ok(())
}
pub fn from_bytes(mut bytes: &[u8]) -> Result<Self> {
let reader: &mut dyn Read = &mut bytes;
let id = Ecc::from(reader.read_u64::<NE>()?);
match id.endian(Self::ID) {
Some(Endian::Big) => Ok(Self::from_reader::<BE>(reader)?),
Some(Endian::Little) => Ok(Self::from_reader::<LE>(reader)?),
None => Err(Error::Invalid("Not a valid hierarchical.".into())),
}
}
fn from_reader<E: ByteOrder>(reader: &mut dyn Read) -> Result<Self> {
let key = Self::read_string::<E>(reader)?;
let content_count = reader.read_u32::<E>()?;
let mut content = vec![];
for _ in 0..content_count {
content.push(Self::read_string::<E>(reader)?);
}
let child_count = reader.read_u32::<E>()?;
let mut children = vec![];
for _ in 0..child_count {
children.push(Self::from_reader::<E>(reader)?);
}
Ok(Self {
key,
content,
children,
})
}
fn read_string<E: ByteOrder>(reader: &mut dyn Read) -> Result<String> {
let len = reader.read_u16::<E>()?;
let mut buffer = vec![0; len as usize];
reader.read_exact(buffer.as_mut_slice())?;
Ok(std::str::from_utf8(buffer.as_slice())?.into())
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_serialization() {
let test = Hierarchical::new(
"Bah",
vec![String::from("1"), String::from("2")],
vec![Hierarchical::new(
"Humbug",
vec![String::from("3"), String::from("4")],
vec![],
)],
);
let bytes = test.clone().to_bytes::<NE>().unwrap();
let result = Hierarchical::from_bytes(bytes.as_slice()).unwrap();
assert_eq!(test, result);
}
}