mod error;
mod reader;
mod type_id;
mod writer;
use std::{
borrow::Borrow,
collections::{btree_map, BTreeMap},
hash::Hash,
io::{Read, Write},
iter::FromIterator,
};
use crate::{Error, Variant};
use self::reader::read_attributes;
use self::writer::write_attributes;
pub(crate) use self::error::AttributeError;
#[derive(Debug, Default, Clone, PartialEq)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(transparent)
)]
pub struct Attributes {
data: BTreeMap<String, Variant>,
}
impl Attributes {
pub fn new() -> Self {
Self::default()
}
pub fn from_reader<R: Read>(reader: R) -> Result<Self, Error> {
Ok(Attributes {
data: read_attributes(reader)?,
})
}
pub fn to_writer<W: Write>(&self, mut writer: W) -> Result<(), Error> {
write_attributes(&self.data, &mut writer).map_err(Into::into)
}
pub fn get<K: Borrow<str>>(&self, key: K) -> Option<&Variant> {
self.data.get(key.borrow())
}
pub fn insert(&mut self, key: String, value: Variant) -> Option<Variant> {
self.data.insert(key, value)
}
pub fn with<K: Into<String>, V: Into<Variant>>(mut self, key: K, value: V) -> Self {
self.data.insert(key.into(), value.into());
self
}
pub fn remove<K: Hash + Eq + Borrow<str>>(&mut self, key: K) -> Option<Variant> {
self.data.remove(key.borrow())
}
pub fn iter(&self) -> impl Iterator<Item = (&String, &Variant)> {
self.data.iter()
}
#[inline]
pub fn len(&self) -> usize {
self.data.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
impl IntoIterator for Attributes {
type IntoIter = AttributesIntoIter;
type Item = (String, Variant);
fn into_iter(self) -> Self::IntoIter {
AttributesIntoIter {
iter: self.data.into_iter(),
}
}
}
impl FromIterator<(String, Variant)> for Attributes {
fn from_iter<T: IntoIterator<Item = (String, Variant)>>(iter: T) -> Self {
Self {
data: iter.into_iter().collect(),
}
}
}
pub struct AttributesIntoIter {
iter: btree_map::IntoIter<String, Variant>,
}
impl Iterator for AttributesIntoIter {
type Item = (String, Variant);
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
#[cfg(test)]
mod tests {
use super::*;
const ATTRIBUTES_BASE64: &str = "\
DQAAAAcAAABCb29sZWFuAwEKAAAAQnJpY2tDb2xvcg7sAwAABgAAAENvbG9yMw+joiI/AAAA\
AAAAgD8NAAAAQ29sb3JTZXF1ZW5jZRkDAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAAAA\
PwAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/BgAAAE51bWJlcgYAAAAAgBzIQAsA\
AABOdW1iZXJSYW5nZRsAAKBAAAAgQQ4AAABOdW1iZXJTZXF1ZW5jZRcDAAAAAAAAAAAAAAAA\
AIA/AAAAAAAAAD8AAAAAAAAAAAAAgD8AAIA/BAAAAFJlY3QcAACAPwAAAEAAAEBAAACAQAYA\
AABTdHJpbmcCDQAAAEhlbGxvLCB3b3JsZCEEAAAAVURpbQkAAAA/ZAAAAAUAAABVRGltMgoA\
AAA/CgAAADMzMz8eAAAABwAAAFZlY3RvcjIQAAAgQQAASEIHAAAAVmVjdG9yMxEAAIA/AAAA\
QAAAQEA=";
#[test]
#[cfg(feature = "serde")]
fn test_round_trip_attributes() {
let attributes_value =
base64::decode(ATTRIBUTES_BASE64).expect("bad base64 for attributes");
let attributes = Attributes::from_reader(&attributes_value[..])
.expect("couldn't deserialize attributes");
insta::assert_yaml_snapshot!(attributes);
let mut new_attribute_bytes = Vec::<u8>::new();
attributes
.to_writer(&mut new_attribute_bytes)
.expect("couldn't write attributes to buffer");
let new_attributes = Attributes::from_reader(new_attribute_bytes.as_slice())
.expect("couldn't deserialize crate produced binary");
assert_eq!(attributes, new_attributes);
}
#[test]
#[cfg(feature = "serde")]
fn test_encode_json() {
use serde_json::{json, Value};
fn assert_json(value: Attributes, expected: Value) {
let encoded = serde_json::to_string(&value).unwrap();
let decoded: Value = serde_json::from_str(&encoded).unwrap();
assert_eq!(decoded, expected);
}
let empty = Attributes::new();
assert_json(empty, json!({}));
let number = Attributes::new().with("hello", 5.0f64);
assert_json(
number,
json!({
"hello": {
"Float64": 5.0
}
}),
);
}
#[test]
fn test_attribute_removal() {
let mut attributes = Attributes::new();
attributes.insert("key".to_owned(), Variant::Bool(true));
assert_eq!(attributes.remove("key"), Some(Variant::Bool(true)));
}
}