use crate::serializer::Serializer; use crate::{RefBareItem, SFVResult}; use std::marker::PhantomData; /// Serializes `Item` field value components incrementally. /// ``` /// use sfv::{RefBareItem, RefItemSerializer}; /// /// let mut serialized_item = String::new(); /// let serializer = RefItemSerializer::new(&mut serialized_item); /// serializer /// .bare_item(&RefBareItem::Integer(11)) /// .unwrap() /// .parameter("foo", &RefBareItem::Boolean(true)) /// .unwrap(); /// assert_eq!(serialized_item, "11;foo"); /// ``` #[derive(Debug)] pub struct RefItemSerializer<'a> { pub buffer: &'a mut String, } impl<'a> RefItemSerializer<'a> { pub fn new(buffer: &'a mut String) -> Self { RefItemSerializer { buffer } } pub fn bare_item(self, bare_item: &RefBareItem) -> SFVResult> { Serializer::serialize_ref_bare_item(bare_item, self.buffer)?; Ok(RefParameterSerializer { buffer: self.buffer, }) } } /// Used by `RefItemSerializer`, `RefListSerializer`, `RefDictSerializer` to serialize a single `Parameter`. #[derive(Debug)] pub struct RefParameterSerializer<'a> { buffer: &'a mut String, } impl<'a> RefParameterSerializer<'a> { pub fn parameter(self, name: &str, value: &RefBareItem) -> SFVResult { Serializer::serialize_ref_parameter(name, value, self.buffer)?; Ok(self) } } /// Serializes `List` field value components incrementally. /// ``` /// use sfv::{RefBareItem, RefListSerializer}; /// /// let mut serialized_item = String::new(); /// let serializer = RefListSerializer::new(&mut serialized_item); /// serializer /// .bare_item(&RefBareItem::Integer(11)) /// .unwrap() /// .parameter("foo", &RefBareItem::Boolean(true)) /// .unwrap() /// .open_inner_list() /// .inner_list_bare_item(&RefBareItem::Token("abc")) /// .unwrap() /// .inner_list_parameter("abc_param", &RefBareItem::Boolean(false)) /// .unwrap() /// .inner_list_bare_item(&RefBareItem::Token("def")) /// .unwrap() /// .close_inner_list() /// .parameter("bar", &RefBareItem::String("val")) /// .unwrap(); /// assert_eq!( /// serialized_item, /// "11;foo, (abc;abc_param=?0 def);bar=\"val\"" /// ); /// ``` #[derive(Debug)] pub struct RefListSerializer<'a> { buffer: &'a mut String, } impl<'a> RefListSerializer<'a> { pub fn new(buffer: &'a mut String) -> Self { RefListSerializer { buffer } } pub fn bare_item(self, bare_item: &RefBareItem) -> SFVResult { if !self.buffer.is_empty() { self.buffer.push_str(", "); } Serializer::serialize_ref_bare_item(bare_item, self.buffer)?; Ok(RefListSerializer { buffer: self.buffer, }) } pub fn parameter(self, name: &str, value: &RefBareItem) -> SFVResult { if self.buffer.is_empty() { return Err("parameters must be serialized after bare item or inner list"); } Serializer::serialize_ref_parameter(name, value, self.buffer)?; Ok(RefListSerializer { buffer: self.buffer, }) } pub fn open_inner_list(self) -> RefInnerListSerializer<'a, Self> { if !self.buffer.is_empty() { self.buffer.push_str(", "); } self.buffer.push('('); RefInnerListSerializer:: { buffer: self.buffer, caller_type: PhantomData, } } } /// Serializes `Dictionary` field value components incrementally. /// ``` /// use sfv::{RefBareItem, RefDictSerializer, Decimal, FromPrimitive}; /// /// let mut serialized_item = String::new(); /// let serializer = RefDictSerializer::new(&mut serialized_item); /// serializer /// .bare_item_member("member1", &RefBareItem::Integer(11)) /// .unwrap() /// .parameter("foo", &RefBareItem::Boolean(true)) /// .unwrap() /// .open_inner_list("member2") /// .unwrap() /// .inner_list_bare_item(&RefBareItem::Token("abc")) /// .unwrap() /// .inner_list_parameter("abc_param", &RefBareItem::Boolean(false)) /// .unwrap() /// .inner_list_bare_item(&RefBareItem::Token("def")) /// .unwrap() /// .close_inner_list() /// .parameter("bar", &RefBareItem::String("val")) /// .unwrap() /// .bare_item_member( /// "member3", /// &RefBareItem::Decimal(Decimal::from_f64(12.34566).unwrap()), /// ) /// .unwrap(); /// assert_eq!( /// serialized_item, /// "member1=11;foo, member2=(abc;abc_param=?0 def);bar=\"val\", member3=12.346" /// ); /// ``` #[derive(Debug)] pub struct RefDictSerializer<'a> { buffer: &'a mut String, } impl<'a> RefDictSerializer<'a> { pub fn new(buffer: &'a mut String) -> Self { RefDictSerializer { buffer } } pub fn bare_item_member(self, name: &str, value: &RefBareItem) -> SFVResult { if !self.buffer.is_empty() { self.buffer.push_str(", "); } Serializer::serialize_key(name, self.buffer)?; if value != &RefBareItem::Boolean(true) { self.buffer.push('='); Serializer::serialize_ref_bare_item(value, self.buffer)?; } Ok(self) } pub fn parameter(self, name: &str, value: &RefBareItem) -> SFVResult { if self.buffer.is_empty() { return Err("parameters must be serialized after bare item or inner list"); } Serializer::serialize_ref_parameter(name, value, self.buffer)?; Ok(RefDictSerializer { buffer: self.buffer, }) } pub fn open_inner_list(self, name: &str) -> SFVResult> { if !self.buffer.is_empty() { self.buffer.push_str(", "); } Serializer::serialize_key(name, self.buffer)?; self.buffer.push_str("=("); Ok(RefInnerListSerializer:: { buffer: self.buffer, caller_type: PhantomData, }) } } /// Used by `RefItemSerializer`, `RefListSerializer`, `RefDictSerializer` to serialize `InnerList`. #[derive(Debug)] pub struct RefInnerListSerializer<'a, T> { buffer: &'a mut String, caller_type: PhantomData, } impl<'a, T: Container<'a>> RefInnerListSerializer<'a, T> { pub fn inner_list_bare_item(self, bare_item: &RefBareItem) -> SFVResult { if !self.buffer.is_empty() & !self.buffer.ends_with('(') { self.buffer.push(' '); } Serializer::serialize_ref_bare_item(bare_item, self.buffer)?; Ok(RefInnerListSerializer { buffer: self.buffer, caller_type: PhantomData, }) } pub fn inner_list_parameter(self, name: &str, value: &RefBareItem) -> SFVResult { if self.buffer.is_empty() { return Err("parameters must be serialized after bare item or inner list"); } Serializer::serialize_ref_parameter(name, value, self.buffer)?; Ok(RefInnerListSerializer { buffer: self.buffer, caller_type: PhantomData, }) } pub fn close_inner_list(self) -> T { self.buffer.push(')'); T::new(self.buffer) } } pub trait Container<'a> { fn new(buffer: &'a mut String) -> Self; } impl<'a> Container<'a> for RefListSerializer<'a> { fn new(buffer: &mut String) -> RefListSerializer { RefListSerializer { buffer } } } impl<'a> Container<'a> for RefDictSerializer<'a> { fn new(buffer: &mut String) -> RefDictSerializer { RefDictSerializer { buffer } } } #[cfg(test)] mod alternative_serializer_tests { use super::*; use crate::{Decimal, FromPrimitive}; #[test] fn test_fast_serialize_item() -> SFVResult<()> { let mut output = String::new(); let ser = RefItemSerializer::new(&mut output); ser.bare_item(&RefBareItem::Token("hello"))? .parameter("abc", &RefBareItem::Boolean(true))?; assert_eq!("hello;abc", output); Ok(()) } #[test] fn test_fast_serialize_list() -> SFVResult<()> { let mut output = String::new(); let ser = RefListSerializer::new(&mut output); ser.bare_item(&RefBareItem::Token("hello"))? .parameter("key1", &RefBareItem::Boolean(true))? .parameter("key2", &RefBareItem::Boolean(false))? .open_inner_list() .inner_list_bare_item(&RefBareItem::String("some_string"))? .inner_list_bare_item(&RefBareItem::Integer(12))? .inner_list_parameter("inner-member-key", &RefBareItem::Boolean(true))? .close_inner_list() .parameter("inner-list-param", &RefBareItem::Token("*"))?; assert_eq!( "hello;key1;key2=?0, (\"some_string\" 12;inner-member-key);inner-list-param=*", output ); Ok(()) } #[test] fn test_fast_serialize_dict() -> SFVResult<()> { let mut output = String::new(); let ser = RefDictSerializer::new(&mut output); ser.bare_item_member("member1", &RefBareItem::Token("hello"))? .parameter("key1", &RefBareItem::Boolean(true))? .parameter("key2", &RefBareItem::Boolean(false))? .bare_item_member("member2", &RefBareItem::Boolean(true))? .parameter( "key3", &RefBareItem::Decimal(Decimal::from_f64(45.4586).unwrap()), )? .parameter("key4", &RefBareItem::String("str"))? .open_inner_list("key5")? .inner_list_bare_item(&RefBareItem::Integer(45))? .inner_list_bare_item(&RefBareItem::Integer(0))? .close_inner_list() .bare_item_member("key6", &RefBareItem::String("foo"))? .open_inner_list("key7")? .inner_list_bare_item(&RefBareItem::ByteSeq("some_string".as_bytes()))? .inner_list_bare_item(&RefBareItem::ByteSeq("other_string".as_bytes()))? .close_inner_list() .parameter("lparam", &RefBareItem::Integer(10))? .bare_item_member("key8", &RefBareItem::Boolean(true))?; assert_eq!( "member1=hello;key1;key2=?0, member2;key3=45.459;key4=\"str\", key5=(45 0), key6=\"foo\", key7=(:c29tZV9zdHJpbmc=: :b3RoZXJfc3RyaW5n:);lparam=10, key8", output ); Ok(()) } }