pub(crate) mod build;
pub(crate) mod stream;
use std::fmt;
use std::io;
use serde::Serialize;
use serde::ser;
use oneyaml_core::Settings;
use crate::emit::scalar::Rules;
use crate::emit::{Start, emit_document, output_rules};
use crate::error::Error;
use crate::value::Value;
use build::ToValue;
use stream::{DocState, Frame, Streamer};
pub fn to_value<T: Serialize>(value: T) -> Result<Value, Error> {
to_value_with(value, &Settings::new())
}
pub fn to_value_with<T: Serialize>(value: T, settings: &Settings) -> Result<Value, Error> {
let v = build_value(&value, settings)?;
if let Err(e) = v.check_depth(settings.get_limits()) {
return Err(e.into_error().finish(None, Vec::new()));
}
Ok(v)
}
fn build_value<T: ?Sized + Serialize>(value: &T, settings: &Settings) -> Result<Value, Error> {
build::build(
value,
ToValue::root(settings.get_limits().get_max_typed_depth()),
)
.map_err(|e| e.finish(None, Vec::new()))
}
impl Value {
pub fn to_yaml_string(&self) -> Result<String, Error> {
self.to_yaml_string_with(&Settings::new())
}
pub fn to_yaml_string_with(&self, settings: &Settings) -> Result<String, Error> {
let rules = output_rules(settings)?;
let mut out = String::new();
emit_document(&mut out, self, &rules, settings.get_limits(), Start::First)
.map_err(|e| e.finish(None, Vec::new()))?;
Ok(out)
}
}
pub fn to_string<T: ?Sized + Serialize>(value: &T) -> Result<String, Error> {
to_string_with(value, &Settings::new())
}
pub fn to_string_with<T: ?Sized + Serialize>(
value: &T,
settings: &Settings,
) -> Result<String, Error> {
let rules = output_rules(settings)?;
let mut doc = DocState::new(settings.get_limits());
doc.document(&rules, Start::First, value)
.map_err(|e| e.finish(None, Vec::new()))?;
Ok(doc.out)
}
pub fn to_writer<W: io::Write, T: ?Sized + Serialize>(writer: W, value: &T) -> Result<(), Error> {
to_writer_with(writer, value, &Settings::new())
}
pub fn to_writer_with<W: io::Write, T: ?Sized + Serialize>(
mut writer: W,
value: &T,
settings: &Settings,
) -> Result<(), Error> {
let out = to_string_with(value, settings)?;
writer.write_all(out.as_bytes()).map_err(Error::write)?;
writer.flush().map_err(Error::write)
}
pub struct Serializer<W> {
writer: W,
settings: Settings,
rules: Option<Rules>,
documents: usize,
doc: DocState,
}
impl<W> fmt::Debug for Serializer<W> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Serializer")
.field("settings", &self.settings)
.field("documents", &self.documents)
.finish_non_exhaustive()
}
}
impl<W: io::Write> Serializer<W> {
pub fn new(writer: W) -> Self {
Self::with_settings(writer, &Settings::new())
}
pub fn with_settings(writer: W, settings: &Settings) -> Self {
Self {
writer,
settings: settings.clone(),
rules: None,
documents: 0,
doc: DocState::new(settings.get_limits()),
}
}
#[must_use]
pub fn documents(&self) -> usize {
self.documents
}
pub fn into_inner(mut self) -> Result<W, Error> {
self.writer.flush().map_err(Error::write)?;
Ok(self.writer)
}
fn parts(&mut self) -> Result<(&mut DocState, &Rules), Error> {
if self.rules.is_none() {
self.rules = Some(output_rules(&self.settings)?);
}
match &self.rules {
Some(rules) => Ok((&mut self.doc, rules)),
None => Err(Error::internal("output rules missing", None)),
}
}
fn start(&self) -> Start {
if self.documents == 0 {
Start::First
} else {
Start::Next
}
}
fn scalar(&mut self, f: impl FnOnce(Streamer<'_>) -> Result<(), Error>) -> Result<(), Error> {
let start = self.start();
let (doc, rules) = self.parts()?;
let r = f(doc.root(rules, start)).and_then(|()| doc.finish());
self.commit(r)
}
fn open(
&mut self,
variant: Option<&'static str>,
seq: bool,
) -> Result<DocumentCompound<'_, W>, Error> {
let start = self.start();
let (doc, rules) = self.parts()?;
let frame = doc.root(rules, start).root_frame(variant, seq)?;
Ok(DocumentCompound { ser: self, frame })
}
fn commit(&mut self, r: Result<(), Error>) -> Result<(), Error> {
r.map_err(|e| e.finish(None, Vec::new()))?;
self.writer
.write_all(self.doc.out.as_bytes())
.map_err(Error::write)?;
self.documents = self.documents.saturating_add(1);
Ok(())
}
}
pub struct DocumentCompound<'a, W> {
ser: &'a mut Serializer<W>,
frame: Frame,
}
impl<W> fmt::Debug for DocumentCompound<'_, W> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DocumentCompound")
.field("ser", &self.ser)
.finish_non_exhaustive()
}
}
impl<W: io::Write> DocumentCompound<'_, W> {
fn with(
&mut self,
f: impl FnOnce(&mut Frame, &mut DocState, &Rules) -> Result<(), Error>,
) -> Result<(), Error> {
let ser = &mut *self.ser;
let r = match &ser.rules {
Some(rules) => f(&mut self.frame, &mut ser.doc, rules),
None => Err(Error::internal("output rules missing", None)),
};
r.map_err(|e| e.finish(None, Vec::new()))
}
fn end_document(self) -> Result<(), Error> {
let ser = self.ser;
let r = match &ser.rules {
Some(rules) => self
.frame
.end(&mut ser.doc, rules)
.and_then(|()| ser.doc.finish()),
None => Err(Error::internal("output rules missing", None)),
};
ser.commit(r)
}
}
macro_rules! scalar_documents {
($($method:ident($t:ty)),* $(,)?) => {$(
fn $method(self, v: $t) -> Result<(), Error> {
self.scalar(|s| ser::Serializer::$method(s, v))
}
)*};
}
impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer<W> {
type Ok = ();
type Error = Error;
type SerializeSeq = DocumentCompound<'a, W>;
type SerializeTuple = DocumentCompound<'a, W>;
type SerializeTupleStruct = DocumentCompound<'a, W>;
type SerializeTupleVariant = DocumentCompound<'a, W>;
type SerializeMap = DocumentCompound<'a, W>;
type SerializeStruct = DocumentCompound<'a, W>;
type SerializeStructVariant = DocumentCompound<'a, W>;
scalar_documents! {
serialize_bool(bool), serialize_i8(i8), serialize_i16(i16), serialize_i32(i32),
serialize_i64(i64), serialize_i128(i128), serialize_u8(u8), serialize_u16(u16),
serialize_u32(u32), serialize_u64(u64), serialize_u128(u128), serialize_f32(f32),
serialize_f64(f64), serialize_char(char), serialize_str(&str), serialize_bytes(&[u8]),
}
fn serialize_none(self) -> Result<(), Error> {
self.scalar(|s| ser::Serializer::serialize_none(s))
}
fn serialize_some<T: ?Sized + Serialize>(self, v: &T) -> Result<(), Error> {
self.scalar(|s| ser::Serializer::serialize_some(s, v))
}
fn serialize_unit(self) -> Result<(), Error> {
self.scalar(|s| ser::Serializer::serialize_unit(s))
}
fn serialize_unit_struct(self, name: &'static str) -> Result<(), Error> {
self.scalar(|s| ser::Serializer::serialize_unit_struct(s, name))
}
fn serialize_unit_variant(
self,
name: &'static str,
i: u32,
variant: &'static str,
) -> Result<(), Error> {
self.scalar(|s| ser::Serializer::serialize_unit_variant(s, name, i, variant))
}
fn serialize_newtype_struct<T: ?Sized + Serialize>(
self,
name: &'static str,
v: &T,
) -> Result<(), Error> {
self.scalar(|s| ser::Serializer::serialize_newtype_struct(s, name, v))
}
fn serialize_newtype_variant<T: ?Sized + Serialize>(
self,
name: &'static str,
i: u32,
variant: &'static str,
v: &T,
) -> Result<(), Error> {
self.scalar(|s| ser::Serializer::serialize_newtype_variant(s, name, i, variant, v))
}
fn serialize_seq(self, _: Option<usize>) -> Result<DocumentCompound<'a, W>, Error> {
self.open(None, true)
}
fn serialize_tuple(self, _: usize) -> Result<DocumentCompound<'a, W>, Error> {
self.open(None, true)
}
fn serialize_tuple_struct(
self,
_: &'static str,
_: usize,
) -> Result<DocumentCompound<'a, W>, Error> {
self.open(None, true)
}
fn serialize_tuple_variant(
self,
_: &'static str,
_: u32,
variant: &'static str,
_: usize,
) -> Result<DocumentCompound<'a, W>, Error> {
self.open(Some(variant), true)
}
fn serialize_map(self, _: Option<usize>) -> Result<DocumentCompound<'a, W>, Error> {
self.open(None, false)
}
fn serialize_struct(self, _: &'static str, _: usize) -> Result<DocumentCompound<'a, W>, Error> {
self.open(None, false)
}
fn serialize_struct_variant(
self,
_: &'static str,
_: u32,
variant: &'static str,
_: usize,
) -> Result<DocumentCompound<'a, W>, Error> {
self.open(Some(variant), false)
}
}
macro_rules! document_compound {
($trait:ident, $method:ident, element) => {
impl<W: io::Write> ser::$trait for DocumentCompound<'_, W> {
type Ok = ();
type Error = Error;
fn $method<T: ?Sized + Serialize>(&mut self, v: &T) -> Result<(), Error> {
self.with(|frame, doc, rules| frame.element(doc, rules, v))
}
fn end(self) -> Result<(), Error> {
self.end_document()
}
}
};
($trait:ident, $method:ident, field) => {
impl<W: io::Write> ser::$trait for DocumentCompound<'_, W> {
type Ok = ();
type Error = Error;
fn $method<T: ?Sized + Serialize>(
&mut self,
name: &'static str,
v: &T,
) -> Result<(), Error> {
self.with(|frame, doc, rules| frame.field(doc, rules, name, v))
}
fn end(self) -> Result<(), Error> {
self.end_document()
}
}
};
}
document_compound!(SerializeSeq, serialize_element, element);
document_compound!(SerializeTuple, serialize_element, element);
document_compound!(SerializeTupleStruct, serialize_field, element);
document_compound!(SerializeTupleVariant, serialize_field, element);
document_compound!(SerializeStruct, serialize_field, field);
document_compound!(SerializeStructVariant, serialize_field, field);
impl<W: io::Write> ser::SerializeMap for DocumentCompound<'_, W> {
type Ok = ();
type Error = Error;
fn serialize_key<T: ?Sized + Serialize>(&mut self, k: &T) -> Result<(), Error> {
self.with(|frame, doc, _| frame.key(doc, k))
}
fn serialize_value<T: ?Sized + Serialize>(&mut self, v: &T) -> Result<(), Error> {
self.with(|frame, doc, rules| frame.value(doc, rules, v))
}
fn end(self) -> Result<(), Error> {
self.end_document()
}
}