Source code
Revision control
Copy as Markdown
Other Tools
use std::borrow::BorrowMut;
use crate::{serializer::Serializer, KeyRef, RefBareItem};
#[cfg(feature = "parsed-types")]
use crate::{Item, ListEntry};
/// Serializes `Item` field value components incrementally.
///
/// Note: The serialization conforms to [RFC 9651], meaning that
/// [`Dates`][crate::Date] and [`Display Strings`][RefBareItem::DisplayString],
/// which cause parsing errors under [RFC 8941], will be serialized
/// unconditionally. The consumer of this API is responsible for determining
/// whether it is valid to serialize these bare items for any specific field.
///
/// ```
/// use sfv::{KeyRef, ItemSerializer};
///
/// # fn main() -> Result<(), sfv::Error> {
/// let serialized_item = ItemSerializer::new()
/// .bare_item(11)
/// .parameter(KeyRef::from_str("foo")?, true)
/// .finish();
///
/// assert_eq!(serialized_item, "11;foo");
/// # Ok(())
/// # }
/// ```
#[derive(Debug)]
#[must_use]
pub struct ItemSerializer<W> {
buffer: W,
}
impl Default for ItemSerializer<String> {
fn default() -> Self {
Self::new()
}
}
impl ItemSerializer<String> {
/// Creates a serializer that writes into a new string.
pub fn new() -> Self {
Self {
buffer: String::new(),
}
}
}
impl<'a> ItemSerializer<&'a mut String> {
/// Creates a serializer that writes into the given string.
pub fn with_buffer(buffer: &'a mut String) -> Self {
Self { buffer }
}
}
impl<W: BorrowMut<String>> ItemSerializer<W> {
/// Serializes the given bare item.
///
/// Returns a serializer for the item's parameters.
pub fn bare_item<'b>(
mut self,
bare_item: impl Into<RefBareItem<'b>>,
) -> ParameterSerializer<W> {
Serializer::serialize_bare_item(bare_item, self.buffer.borrow_mut());
ParameterSerializer {
buffer: self.buffer,
}
}
}
/// Serializes parameters incrementally.
#[derive(Debug)]
#[must_use]
pub struct ParameterSerializer<W> {
buffer: W,
}
impl<W: BorrowMut<String>> ParameterSerializer<W> {
/// Serializes a parameter with the given name and value.
///
/// Returns the serializer.
pub fn parameter<'b>(mut self, name: &KeyRef, value: impl Into<RefBareItem<'b>>) -> Self {
Serializer::serialize_parameter(name, value, self.buffer.borrow_mut());
self
}
/// Serializes the given parameters.
///
/// Returns the serializer.
pub fn parameters<'b>(
mut self,
params: impl IntoIterator<Item = (impl AsRef<KeyRef>, impl Into<RefBareItem<'b>>)>,
) -> Self {
for (name, value) in params {
Serializer::serialize_parameter(name.as_ref(), value, self.buffer.borrow_mut());
}
self
}
/// Finishes parameter serialization and returns the serializer's output.
#[must_use]
pub fn finish(self) -> W {
self.buffer
}
}
fn maybe_write_separator(buffer: &mut String, first: &mut bool) {
if *first {
*first = false;
} else {
buffer.push_str(", ");
}
}
/// Serializes `List` field value components incrementally.
///
/// Note: The serialization conforms to [RFC 9651], meaning that
/// [`Dates`][crate::Date] and [`Display Strings`][RefBareItem::DisplayString],
/// which cause parsing errors under [RFC 8941], will be serialized
/// unconditionally. The consumer of this API is responsible for determining
/// whether it is valid to serialize these bare items for any specific field.
///
/// ```
/// use sfv::{KeyRef, StringRef, TokenRef, ListSerializer};
///
/// # fn main() -> Result<(), sfv::Error> {
/// let mut ser = ListSerializer::new();
///
/// ser.bare_item(11)
/// .parameter(KeyRef::from_str("foo")?, true);
///
/// {
/// let mut ser = ser.inner_list();
///
/// ser.bare_item(TokenRef::from_str("abc")?)
/// .parameter(KeyRef::from_str("abc_param")?, false);
///
/// ser.bare_item(TokenRef::from_str("def")?);
///
/// ser.finish()
/// .parameter(KeyRef::from_str("bar")?, StringRef::from_str("val")?);
/// }
///
/// assert_eq!(
/// ser.finish().as_deref(),
/// Some(r#"11;foo, (abc;abc_param=?0 def);bar="val""#),
/// );
/// # Ok(())
/// # }
/// ```
#[derive(Debug)]
#[must_use]
pub struct ListSerializer<W> {
buffer: W,
first: bool,
}
impl Default for ListSerializer<String> {
fn default() -> Self {
Self::new()
}
}
impl ListSerializer<String> {
/// Creates a serializer that writes into a new string.
pub fn new() -> Self {
Self {
buffer: String::new(),
first: true,
}
}
}
impl<'a> ListSerializer<&'a mut String> {
/// Creates a serializer that writes into the given string.
pub fn with_buffer(buffer: &'a mut String) -> Self {
Self {
buffer,
first: true,
}
}
}
impl<W: BorrowMut<String>> ListSerializer<W> {
/// Serializes the given bare item as a member of the list.
///
/// Returns a serializer for the item's parameters.
pub fn bare_item<'b>(
&mut self,
bare_item: impl Into<RefBareItem<'b>>,
) -> ParameterSerializer<&mut String> {
let buffer = self.buffer.borrow_mut();
maybe_write_separator(buffer, &mut self.first);
Serializer::serialize_bare_item(bare_item, buffer);
ParameterSerializer { buffer }
}
/// Opens an inner list, returning a serializer to be used for its items and
/// parameters.
pub fn inner_list(&mut self) -> InnerListSerializer {
let buffer = self.buffer.borrow_mut();
maybe_write_separator(buffer, &mut self.first);
buffer.push('(');
InnerListSerializer {
buffer: Some(buffer),
}
}
/// Serializes the given members of the list.
#[cfg(feature = "parsed-types")]
pub fn members<'b>(&mut self, members: impl IntoIterator<Item = &'b ListEntry>) {
for value in members {
match value {
ListEntry::Item(value) => {
_ = self.bare_item(&value.bare_item).parameters(&value.params);
}
ListEntry::InnerList(value) => {
let mut ser = self.inner_list();
ser.items(&value.items);
_ = ser.finish().parameters(&value.params);
}
}
}
}
/// Finishes serialization of the list and returns the underlying output.
///
/// Returns `None` if and only if no members were serialized, as [empty
/// lists are not meant to be serialized at
#[must_use]
pub fn finish(self) -> Option<W> {
if self.first {
None
} else {
Some(self.buffer)
}
}
}
/// Serializes `Dictionary` field value components incrementally.
///
/// Note: The serialization conforms to [RFC 9651], meaning that
/// [`Dates`][crate::Date] and [`Display Strings`][RefBareItem::DisplayString],
/// which cause parsing errors under [RFC 8941], will be serialized
/// unconditionally. The consumer of this API is responsible for determining
/// whether it is valid to serialize these bare items for any specific field.
///
///
/// ```
/// use sfv::{KeyRef, StringRef, TokenRef, DictSerializer, Decimal};
///
/// # fn main() -> Result<(), sfv::Error> {
/// let mut ser = DictSerializer::new();
///
/// ser.bare_item(KeyRef::from_str("member1")?, 11)
/// .parameter(KeyRef::from_str("foo")?, true);
///
/// {
/// let mut ser = ser.inner_list(KeyRef::from_str("member2")?);
///
/// ser.bare_item(TokenRef::from_str("abc")?)
/// .parameter(KeyRef::from_str("abc_param")?, false);
///
/// ser.bare_item(TokenRef::from_str("def")?);
///
/// ser.finish()
/// .parameter(KeyRef::from_str("bar")?, StringRef::from_str("val")?);
/// }
///
/// ser.bare_item(KeyRef::from_str("member3")?, Decimal::try_from(12.34566)?);
///
/// assert_eq!(
/// ser.finish().as_deref(),
/// Some(r#"member1=11;foo, member2=(abc;abc_param=?0 def);bar="val", member3=12.346"#),
/// );
/// # Ok(())
/// # }
/// ```
#[derive(Debug)]
#[must_use]
pub struct DictSerializer<W> {
buffer: W,
first: bool,
}
impl Default for DictSerializer<String> {
fn default() -> Self {
Self::new()
}
}
impl DictSerializer<String> {
/// Creates a serializer that writes into a new string.
pub fn new() -> Self {
Self {
buffer: String::new(),
first: true,
}
}
}
impl<'a> DictSerializer<&'a mut String> {
/// Creates a serializer that writes into the given string.
pub fn with_buffer(buffer: &'a mut String) -> Self {
Self {
buffer,
first: true,
}
}
}
impl<W: BorrowMut<String>> DictSerializer<W> {
/// Serializes the given bare item as a member of the dictionary with the
/// given key.
///
/// Returns a serializer for the item's parameters.
pub fn bare_item<'b>(
&mut self,
name: &KeyRef,
value: impl Into<RefBareItem<'b>>,
) -> ParameterSerializer<&mut String> {
let buffer = self.buffer.borrow_mut();
maybe_write_separator(buffer, &mut self.first);
Serializer::serialize_key(name, buffer);
let value = value.into();
if value != RefBareItem::Boolean(true) {
buffer.push('=');
Serializer::serialize_bare_item(value, buffer);
}
ParameterSerializer { buffer }
}
/// Opens an inner list with the given key, returning a serializer to be
/// used for its items and parameters.
pub fn inner_list(&mut self, name: &KeyRef) -> InnerListSerializer {
let buffer = self.buffer.borrow_mut();
maybe_write_separator(buffer, &mut self.first);
Serializer::serialize_key(name, buffer);
buffer.push_str("=(");
InnerListSerializer {
buffer: Some(buffer),
}
}
/// Serializes the given members of the dictionary.
#[cfg(feature = "parsed-types")]
pub fn members<'b>(
&mut self,
members: impl IntoIterator<Item = (impl AsRef<KeyRef>, &'b ListEntry)>,
) {
for (name, value) in members {
match value {
ListEntry::Item(value) => {
_ = self
.bare_item(name.as_ref(), &value.bare_item)
.parameters(&value.params);
}
ListEntry::InnerList(value) => {
let mut ser = self.inner_list(name.as_ref());
ser.items(&value.items);
_ = ser.finish().parameters(&value.params);
}
}
}
}
/// Finishes serialization of the dictionary and returns the underlying output.
///
/// Returns `None` if and only if no members were serialized, as [empty
/// dictionaries are not meant to be serialized at
#[must_use]
pub fn finish(self) -> Option<W> {
if self.first {
None
} else {
Some(self.buffer)
}
}
}
/// Serializes inner lists incrementally.
///
/// The inner list will be closed automatically when the serializer is dropped.
/// To set the inner list's parameters, call [`InnerListSerializer::finish`].
///
/// Failing to drop the serializer or call its `finish` method will result in
/// an invalid serialization that lacks a closing `)` character.
#[derive(Debug)]
#[must_use]
pub struct InnerListSerializer<'a> {
buffer: Option<&'a mut String>,
}
impl Drop for InnerListSerializer<'_> {
fn drop(&mut self) {
if let Some(ref mut buffer) = self.buffer {
buffer.push(')');
}
}
}
impl<'a> InnerListSerializer<'a> {
/// Serializes the given bare item as a member of the inner list.
///
/// Returns a serializer for the item's parameters.
#[allow(clippy::missing_panics_doc)] // The unwrap is safe by construction.
pub fn bare_item<'b>(
&mut self,
bare_item: impl Into<RefBareItem<'b>>,
) -> ParameterSerializer<&mut String> {
let buffer = self.buffer.as_mut().unwrap();
if !buffer.is_empty() && !buffer.ends_with('(') {
buffer.push(' ');
}
Serializer::serialize_bare_item(bare_item, buffer);
ParameterSerializer { buffer }
}
/// Serializes the given items as members of the inner list.
#[cfg(feature = "parsed-types")]
pub fn items<'b>(&mut self, items: impl IntoIterator<Item = &'b Item>) {
for item in items {
_ = self.bare_item(&item.bare_item).parameters(&item.params);
}
}
/// Closes the inner list and returns a serializer for its parameters.
#[allow(clippy::missing_panics_doc)]
pub fn finish(mut self) -> ParameterSerializer<&'a mut String> {
let buffer = self.buffer.take().unwrap();
buffer.push(')');
ParameterSerializer { buffer }
}
}