use deser_core::ser::{self, SerializeDriver, SerializeRef};
use deser_core::{BytesFormat, Error, Serialize};
use crate::emit::Emitter;
use crate::resolve::Version;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Indent {
None,
Spaces(usize),
}
impl Default for Indent {
fn default() -> Indent {
Indent::Spaces(2)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum QuoteStyle {
#[default]
Single,
Double,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum MultilineStyle {
#[default]
Literal,
Quoted,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum FlowPolicy {
#[default]
Never,
LeafIfFits(usize),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum NullStyle {
#[default]
Null,
Tilde,
Empty,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SerializerConfig {
pub(crate) indent: Indent,
pub(crate) indent_sequences: bool,
pub(crate) flow: FlowPolicy,
pub(crate) fold_width: Option<usize>,
pub(crate) quote_style: QuoteStyle,
pub(crate) quote_all: bool,
pub(crate) multiline: MultilineStyle,
pub(crate) null_style: NullStyle,
pub(crate) compat: Version,
pub(crate) binary: bool,
pub(crate) timestamp_tag: bool,
pub(crate) document_start: bool,
pub(crate) version_directive: bool,
pub(crate) end_documents: bool,
context: deser_core::Context,
}
impl Default for SerializerConfig {
fn default() -> SerializerConfig {
SerializerConfig::new()
}
}
impl SerializerConfig {
pub const fn new() -> SerializerConfig {
SerializerConfig {
indent: Indent::Spaces(2),
indent_sequences: true,
flow: FlowPolicy::Never,
fold_width: None,
quote_style: QuoteStyle::Single,
quote_all: false,
multiline: MultilineStyle::Literal,
null_style: NullStyle::Null,
compat: Version::V1_1,
binary: true,
timestamp_tag: false,
document_start: false,
version_directive: false,
end_documents: false,
context: deser_core::Context::new(),
}
}
pub const fn builder() -> SerializerConfigBuilder {
SerializerConfigBuilder::new()
}
pub const fn into_builder(self) -> SerializerConfigBuilder {
SerializerConfigBuilder { value: self }
}
pub fn set_context(&mut self, context: deser_core::Context) {
self.context = context;
}
pub fn context(&self) -> &deser_core::Context {
&self.context
}
#[inline]
fn apply_context(&self, driver: &mut SerializeDriver<'_>) {
if !self.context.is_empty() {
driver.set_default_context(self.context.clone());
}
}
pub const fn set_indent(&mut self, indent: Indent) {
self.indent = match indent {
Indent::None => Indent::None,
Indent::Spaces(0) => Indent::Spaces(1),
Indent::Spaces(n) if n > 9 => Indent::Spaces(9),
Indent::Spaces(n) => Indent::Spaces(n),
};
}
pub(crate) fn indent_width(&self) -> usize {
match self.indent {
Indent::Spaces(n) => n,
Indent::None => 0,
}
}
pub const fn set_indent_sequences(&mut self, yes: bool) {
self.indent_sequences = yes;
}
pub const fn set_flow(&mut self, policy: FlowPolicy) {
self.flow = policy;
}
pub const fn set_fold_width(&mut self, width: Option<usize>) {
self.fold_width = width;
}
pub const fn set_quote_style(&mut self, style: QuoteStyle) {
self.quote_style = style;
}
pub const fn set_quote_all(&mut self, yes: bool) {
self.quote_all = yes;
}
pub const fn set_multiline(&mut self, style: MultilineStyle) {
self.multiline = style;
}
pub const fn set_null_style(&mut self, style: NullStyle) {
self.null_style = style;
}
pub const fn set_compat(&mut self, version: Version) {
self.compat = version;
}
pub const fn set_binary(&mut self, yes: bool) {
self.binary = yes;
}
pub const fn set_timestamp_tag(&mut self, yes: bool) {
self.timestamp_tag = yes;
}
pub const fn set_document_start(&mut self, yes: bool) {
self.document_start = yes;
}
pub const fn set_version_directive(&mut self, yes: bool) {
self.version_directive = yes;
}
pub const fn set_end_documents(&mut self, yes: bool) {
self.end_documents = yes;
}
pub(crate) fn emitter(&self, index: usize, mut out: String, bytes: BytesFormat) -> Emitter {
if self.version_directive {
if index > 0 && !self.end_documents {
out.push_str("...\n");
}
out.push_str("%YAML 1.2\n---\n");
} else if self.document_start || index > 0 {
out.push_str("---\n");
}
Emitter::new(self, out, bytes)
}
pub(crate) fn end_document(&self, emitter: &mut Emitter) -> Result<(), Error> {
emitter.finish()?;
if self.end_documents {
emitter.out.push_str("...\n");
}
Ok(())
}
pub(crate) fn document_whole(
&self,
index: usize,
driver: &mut SerializeDriver<'_>,
out: &mut String,
) -> Result<(), Error> {
let len = out.len();
let mut emitter = self.emitter(index, std::mem::take(out), BytesFormat::of(driver.state()));
let rv = driver
.drive(|event, state| emitter.event(event, state))
.and_then(|()| self.end_document(&mut emitter));
*out = emitter.out;
if rv.is_err() {
out.truncate(len);
}
rv
}
pub(crate) fn document_part(
&self,
index: usize,
document: &mut Option<Box<Emitter>>,
driver: &mut SerializeDriver<'_>,
out: &mut String,
limit: usize,
) -> Result<bool, Error> {
if document.is_none() && limit == usize::MAX {
return self.document_whole(index, driver, out).map(|()| true);
}
let len = out.len();
let adopt = out.is_empty();
let mut emitter = match document.take() {
Some(mut emitter) => {
if adopt {
emitter.out = std::mem::take(out);
}
emitter
}
None => {
let buffer = match adopt {
true => std::mem::take(out),
false => String::new(),
};
Box::new(self.emitter(index, buffer, BytesFormat::of(driver.state())))
}
};
emitter.limit = limit;
let rv = driver.drive_until(&mut *emitter).and_then(|done| {
if done {
self.end_document(&mut emitter)?;
}
Ok(done)
});
let done = match rv {
Ok(done) => done,
Err(err) => {
if adopt {
*out = std::mem::take(&mut emitter.out);
}
out.truncate(len);
return Err(err);
}
};
let output = emitter.take_output();
if adopt {
*out = output;
} else {
out.push_str(&output);
}
if !done {
*document = Some(emitter);
}
Ok(done)
}
pub fn to_string<T: Serialize + ?Sized>(&self, value: &T) -> Result<String, Error> {
self.to_string_ref(SerializeRef::new(&value))
}
pub fn to_string_with<F, T: Serialize + ?Sized>(
&self,
value: &T,
setup: F,
) -> Result<String, Error>
where
F: FnOnce(&mut SerializeDriver<'_>),
{
let mut driver = SerializeDriver::new(&value);
setup(&mut driver);
self.apply_context(&mut driver);
let mut out = String::new();
self.document_whole(0, &mut driver, &mut out)?;
Ok(out)
}
fn to_string_ref(&self, value: SerializeRef<'_>) -> Result<String, Error> {
let mut driver = SerializeDriver::from_ref(value);
self.apply_context(&mut driver);
let mut out = String::new();
self.document_whole(0, &mut driver, &mut out)?;
Ok(out)
}
}
#[derive(Debug, Clone)]
#[must_use]
pub struct SerializerConfigBuilder {
value: SerializerConfig,
}
impl SerializerConfigBuilder {
pub const fn new() -> SerializerConfigBuilder {
SerializerConfigBuilder {
value: SerializerConfig::new(),
}
}
pub const fn indent(mut self, indent: Indent) -> SerializerConfigBuilder {
self.value.set_indent(indent);
self
}
pub const fn indent_sequences(mut self, yes: bool) -> SerializerConfigBuilder {
self.value.set_indent_sequences(yes);
self
}
pub const fn flow(mut self, policy: FlowPolicy) -> SerializerConfigBuilder {
self.value.set_flow(policy);
self
}
pub const fn fold_width(mut self, width: Option<usize>) -> SerializerConfigBuilder {
self.value.set_fold_width(width);
self
}
pub const fn quote_style(mut self, style: QuoteStyle) -> SerializerConfigBuilder {
self.value.set_quote_style(style);
self
}
pub const fn quote_all(mut self, yes: bool) -> SerializerConfigBuilder {
self.value.set_quote_all(yes);
self
}
pub const fn multiline(mut self, style: MultilineStyle) -> SerializerConfigBuilder {
self.value.set_multiline(style);
self
}
pub const fn null_style(mut self, style: NullStyle) -> SerializerConfigBuilder {
self.value.set_null_style(style);
self
}
pub const fn compat(mut self, version: Version) -> SerializerConfigBuilder {
self.value.set_compat(version);
self
}
pub const fn binary(mut self, yes: bool) -> SerializerConfigBuilder {
self.value.set_binary(yes);
self
}
pub const fn timestamp_tag(mut self, yes: bool) -> SerializerConfigBuilder {
self.value.set_timestamp_tag(yes);
self
}
pub const fn document_start(mut self, yes: bool) -> SerializerConfigBuilder {
self.value.set_document_start(yes);
self
}
pub const fn version_directive(mut self, yes: bool) -> SerializerConfigBuilder {
self.value.set_version_directive(yes);
self
}
pub const fn end_documents(mut self, yes: bool) -> SerializerConfigBuilder {
self.value.set_end_documents(yes);
self
}
pub fn context(mut self, context: deser_core::Context) -> SerializerConfigBuilder {
self.value.set_context(context);
self
}
pub const fn build(self) -> SerializerConfig {
let value = unsafe { core::ptr::read(&self.value) };
core::mem::forget(self);
value
}
}
impl Default for SerializerConfigBuilder {
fn default() -> SerializerConfigBuilder {
SerializerConfigBuilder::new()
}
}
pub struct Serializer {
config: SerializerConfig,
out: String,
written: usize,
document: Option<Box<Emitter>>,
in_progress: bool,
}
impl Default for Serializer {
fn default() -> Serializer {
Serializer::new()
}
}
impl Clone for Serializer {
fn clone(&self) -> Serializer {
Serializer {
config: self.config.clone(),
out: self.out.clone(),
written: self.written,
document: None,
in_progress: self.in_progress,
}
}
}
impl std::fmt::Debug for Serializer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Serializer")
.field("config", &self.config)
.field("output", &self.out)
.field("written", &self.written)
.field("in_progress", &self.in_progress)
.finish()
}
}
impl Serializer {
pub fn new() -> Serializer {
Serializer::with_config(SerializerConfig::new())
}
pub fn with_config(config: SerializerConfig) -> Serializer {
Serializer::with_written(config, 0)
}
pub fn with_written(config: SerializerConfig, written: usize) -> Serializer {
Serializer {
config,
out: String::new(),
written,
document: None,
in_progress: false,
}
}
pub fn config(&self) -> &SerializerConfig {
&self.config
}
pub fn written(&self) -> usize {
self.written
}
pub fn serialize<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
ser::Serializer::serialize(self, value)
}
pub fn serialize_with<F, T: Serialize + ?Sized>(
&mut self,
value: &T,
setup: F,
) -> Result<(), Error>
where
F: FnOnce(&mut SerializeDriver<'_>),
{
ser::Serializer::serialize_with(self, value, setup)
}
pub fn as_str(&self) -> &str {
&self.out
}
pub fn finish(self) -> String {
self.out
}
}
impl ser::Serializer for Serializer {
fn drive(&mut self, driver: &mut SerializeDriver<'_>) -> Result<(), Error> {
if self.in_progress {
return Err(Error::in_progress());
}
ser::StreamSerializer::drive_partial(self, driver, usize::MAX).map(|_| ())
}
}
impl ser::StreamSerializer for Serializer {
fn output(&self) -> &[u8] {
self.out.as_bytes()
}
fn clear_output(&mut self) {
self.out.clear();
}
fn supports_partial(&self) -> bool {
true
}
fn drive_partial(
&mut self,
driver: &mut SerializeDriver<'_>,
limit: usize,
) -> Result<bool, Error> {
if !self.config.context.is_empty() {
driver.set_default_context(self.config.context.clone());
}
if self.document.is_none() && self.in_progress {
return Err(Error::in_progress());
}
if !self.config.document_part(
self.written,
&mut self.document,
driver,
&mut self.out,
limit,
)? {
self.in_progress = true;
return Ok(false);
}
self.in_progress = false;
self.written += 1;
Ok(true)
}
fn in_progress(&self) -> bool {
self.in_progress
}
}
#[cfg(feature = "io")]
impl SerializerConfig {
pub fn writer<W: std::io::Write>(&self, writer: W) -> deser_core::io::Writer<W, Serializer> {
deser_core::io::Writer::new(writer, Serializer::with_config(self.clone()))
}
pub fn to_writer<W: std::io::Write, T: Serialize + ?Sized>(
&self,
writer: W,
value: &T,
) -> Result<(), Error> {
self.writer(writer).write(value)
}
}
#[cfg(feature = "io")]
pub fn to_writer<W: std::io::Write, T: Serialize + ?Sized>(
writer: W,
value: &T,
) -> Result<(), Error> {
SerializerConfig::new().to_writer(writer, value)
}
pub fn to_string<T: Serialize + ?Sized>(value: &T) -> Result<String, Error> {
SerializerConfig::new().to_string(value)
}