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 const fn new() -> Self {
Self {
data: BTreeMap::new(),
}
}
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())
}
#[inline]
pub fn clear(&mut self) {
self.data.clear()
}
#[inline]
pub fn iter(&self) -> AttributesIter<'_> {
AttributesIter {
iter: self.data.iter(),
}
}
#[inline]
pub fn drain(&mut self) -> AttributesDrain<'_> {
AttributesDrain { inner: self }
}
#[inline]
pub fn len(&self) -> usize {
self.data.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
impl Extend<(String, Variant)> for Attributes {
fn extend<T: IntoIterator<Item = (String, Variant)>>(&mut self, iter: T) {
self.data.extend(iter)
}
}
impl IntoIterator for Attributes {
type IntoIter = AttributesIntoIter;
type Item = (String, Variant);
fn into_iter(self) -> Self::IntoIter {
AttributesIntoIter {
iter: self.data.into_iter(),
}
}
}
impl<'a> IntoIterator for &'a Attributes {
type IntoIter = AttributesIter<'a>;
type Item = (&'a String, &'a Variant);
fn into_iter(self) -> Self::IntoIter {
AttributesIter {
iter: self.data.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()
}
}
pub struct AttributesIter<'a> {
iter: btree_map::Iter<'a, String, Variant>,
}
impl<'a> Iterator for AttributesIter<'a> {
type Item = (&'a String, &'a Variant);
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
pub struct AttributesDrain<'a> {
inner: &'a mut Attributes,
}
impl Iterator for AttributesDrain<'_> {
type Item = (String, Variant);
fn next(&mut self) -> Option<Self::Item> {
self.inner.data.pop_first()
}
}
impl Drop for AttributesDrain<'_> {
fn drop(&mut self) {
self.inner.clear()
}
}
#[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)));
}
#[test]
fn attribute_drain() {
let mut attributes = Attributes::new();
attributes.extend([
("string".into(), "value".into()),
("float64".into(), 10.0_f64.into()),
("bool".into(), true.into()),
]);
assert!(!attributes.is_empty());
let mut map = BTreeMap::new();
for (key, value) in attributes.drain() {
map.insert(key, value);
}
assert_eq!(map.get("string"), Some(&Variant::String("value".into())));
assert_eq!(map.get("float64"), Some(&Variant::Float64(10.0)));
assert_eq!(map.get("bool"), Some(&Variant::Bool(true)));
assert!(attributes.is_empty());
}
}