Skip to main content

deser_yaml/
ser.rs

1use deser_core::ser::{self, SerializeDriver, SerializeRef};
2use deser_core::{BytesFormat, Error, Serialize};
3
4use crate::emit::Emitter;
5use crate::resolve::Version;
6
7/// How the output is indented.
8///
9/// See [`SerializerConfig::set_indent`].
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11#[non_exhaustive]
12pub enum Indent {
13    /// No indentation: the document is written on a single line in flow
14    /// style (`{name: web, ports: [80, 443]}`).
15    None,
16    /// Block style indented by the given number of spaces per level.
17    ///
18    /// YAML does not allow tabs for indentation.  Values outside of
19    /// `1..=9` are clamped as indentation indicators of block scalars are
20    /// single digits.
21    Spaces(usize),
22}
23
24impl Default for Indent {
25    fn default() -> Indent {
26        Indent::Spaces(2)
27    }
28}
29
30/// How strings are quoted when they cannot be written plain.
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
32#[non_exhaustive]
33pub enum QuoteStyle {
34    /// Single quotes (`'yes'`), double quotes if the string needs escapes.
35    #[default]
36    Single,
37    /// Double quotes (`"yes"`).
38    Double,
39}
40
41/// How strings with line breaks are written.
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
43#[non_exhaustive]
44pub enum MultilineStyle {
45    /// As literal block scalars (`|`) if they only contain characters that
46    /// can be written in them, otherwise double-quoted.
47    #[default]
48    Literal,
49    /// Double-quoted with escapes (`"a\nb"`).
50    Quoted,
51}
52
53/// When collections are written in flow style (`[a, b]`, `{a: 1}`).
54///
55/// Collections with the [`Layout::Compact`](deser_core::hints::Layout) hint are
56/// always written in flow style, collections with
57/// [`Layout::Expanded`](deser_core::hints::Layout) never (unless they are in a
58/// flow collection, which can only contain flow collections).
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
60#[non_exhaustive]
61pub enum FlowPolicy {
62    /// Only compact collections are written in flow style.
63    #[default]
64    Never,
65    /// Collections which only contain scalars are written in flow style if
66    /// they end before the given column.
67    LeafIfFits(usize),
68}
69
70/// How null is written.
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
72#[non_exhaustive]
73pub enum NullStyle {
74    /// `null`
75    #[default]
76    Null,
77    /// `~`
78    Tilde,
79    /// Nothing (`key:`, `-`).  Null keys and null documents are written as
80    /// `null`.
81    Empty,
82}
83
84/// Configures how values are serialized to YAML.
85///
86/// YAML allows the same data to be written in many ways.  The defaults
87/// follow what is common for hand-written YAML:
88///
89/// * block collections indented by two spaces (see [`set_indent`](Self::set_indent)),
90///   sequences in mappings are indented
91///   ([`set_indent_sequences`](Self::set_indent_sequences)), empty collections are
92///   written as `{}` and `[]`.  Compact collections (see
93///   [`hints`](deser_core::hints)) are written in flow style, see
94///   [`set_flow`](Self::set_flow).
95/// * strings are plain if possible, otherwise single-quoted (double-quoted if
96///   they need escapes).  Strings are quoted if readers of YAML 1.1 would
97///   read them as something else (`yes`, `0777`, timestamps, see
98///   [`set_compat`](Self::set_compat)).
99/// * strings with line breaks are literal block scalars (`|`), the style of
100///   individual strings can be requested (see [`style`](crate::style)).
101/// * bytes are written as `!!binary` (see [`set_binary`](Self::set_binary)).
102///
103/// ```
104/// use std::collections::BTreeMap;
105/// use deser_yaml::SerializerConfig;
106///
107/// let mut value = BTreeMap::new();
108/// value.insert("items", vec!["a", "yes"]);
109/// assert_eq!(
110///     deser_yaml::to_string(&value).unwrap(),
111///     "items:\n  - a\n  - 'yes'\n"
112/// );
113///
114/// const INDENTLESS: SerializerConfig =
115///     SerializerConfig::builder().indent_sequences(false).build();
116/// assert_eq!(
117///     INDENTLESS.to_string(&value).unwrap(),
118///     "items:\n- a\n- 'yes'\n"
119/// );
120/// ```
121///
122/// [`to_string`](Self::to_string) works like the
123/// [`to_string`] function.
124#[derive(Debug, Clone, PartialEq, Eq)]
125pub struct SerializerConfig {
126    pub(crate) indent: Indent,
127    pub(crate) indent_sequences: bool,
128    pub(crate) flow: FlowPolicy,
129    pub(crate) fold_width: Option<usize>,
130    pub(crate) quote_style: QuoteStyle,
131    pub(crate) quote_all: bool,
132    pub(crate) multiline: MultilineStyle,
133    pub(crate) null_style: NullStyle,
134    pub(crate) compat: Version,
135    pub(crate) binary: bool,
136    pub(crate) timestamp_tag: bool,
137    pub(crate) document_start: bool,
138    pub(crate) version_directive: bool,
139    pub(crate) end_documents: bool,
140    context: deser_core::Context,
141}
142
143impl Default for SerializerConfig {
144    fn default() -> SerializerConfig {
145        SerializerConfig::new()
146    }
147}
148
149impl SerializerConfig {
150    /// Creates the default configuration.
151    pub const fn new() -> SerializerConfig {
152        SerializerConfig {
153            indent: Indent::Spaces(2),
154            indent_sequences: true,
155            flow: FlowPolicy::Never,
156            fold_width: None,
157            quote_style: QuoteStyle::Single,
158            quote_all: false,
159            multiline: MultilineStyle::Literal,
160            null_style: NullStyle::Null,
161            compat: Version::V1_1,
162            binary: true,
163            timestamp_tag: false,
164            document_start: false,
165            version_directive: false,
166            end_documents: false,
167            context: deser_core::Context::new(),
168        }
169    }
170
171    /// Returns a builder for the configuration (see [`SerializerConfigBuilder`]).
172    pub const fn builder() -> SerializerConfigBuilder {
173        SerializerConfigBuilder::new()
174    }
175
176    /// Returns a builder that starts with this configuration.
177    pub const fn into_builder(self) -> SerializerConfigBuilder {
178        SerializerConfigBuilder { value: self }
179    }
180
181    /// Sets the context the values are serialized in.
182    ///
183    /// The values of the context are the defaults of the extension values
184    /// of the state (see [`Context`](deser_core::Context)), for instance
185    /// the [`BytesFormat`](deser_core::BytesFormat).  The serializers and
186    /// writers created with the configuration use this context.  A context set on
187    /// the driver takes precedence.
188    pub fn set_context(&mut self, context: deser_core::Context) {
189        self.context = context;
190    }
191
192    /// Returns the context the values are serialized in.
193    pub fn context(&self) -> &deser_core::Context {
194        &self.context
195    }
196
197    /// Gives the context to a driver which has none.
198    #[inline]
199    fn apply_context(&self, driver: &mut SerializeDriver<'_>) {
200        if !self.context.is_empty() {
201            driver.set_default_context(self.context.clone());
202        }
203    }
204
205    /// Sets how the output is indented.
206    ///
207    /// The default is [`Indent::Spaces(2)`](Indent::Spaces), values outside
208    /// of `1..=9` are clamped.  With [`Indent::None`] documents are written
209    /// on a single line in flow style, the [flow policy](Self::set_flow) and
210    /// [`Layout`](deser_core::hints::Layout) hints have no effect then:
211    ///
212    /// ```
213    /// use deser::Serialize;
214    /// use deser_yaml::{Indent, SerializerConfig};
215    ///
216    /// #[derive(Serialize)]
217    /// struct Config {
218    ///     name: String,
219    ///     ports: Vec<u16>,
220    /// }
221    ///
222    /// let config = Config { name: "web".into(), ports: vec![80, 443] };
223    /// const WIDE: SerializerConfig =
224    ///     SerializerConfig::builder().indent(Indent::Spaces(4)).build();
225    /// assert_eq!(
226    ///     WIDE.to_string(&config).unwrap(),
227    ///     "name: web\nports:\n    - 80\n    - 443\n"
228    /// );
229    /// const LINE: SerializerConfig =
230    ///     SerializerConfig::builder().indent(Indent::None).build();
231    /// assert_eq!(
232    ///     LINE.to_string(&config).unwrap(),
233    ///     "{name: web, ports: [80, 443]}\n"
234    /// );
235    /// ```
236    pub const fn set_indent(&mut self, indent: Indent) {
237        self.indent = match indent {
238            Indent::None => Indent::None,
239            Indent::Spaces(0) => Indent::Spaces(1),
240            Indent::Spaces(n) if n > 9 => Indent::Spaces(9),
241            Indent::Spaces(n) => Indent::Spaces(n),
242        };
243    }
244
245    /// Returns the number of spaces per indentation level of block
246    /// collections.
247    pub(crate) fn indent_width(&self) -> usize {
248        match self.indent {
249            Indent::Spaces(n) => n,
250            // nothing is written in block style
251            Indent::None => 0,
252        }
253    }
254
255    /// Indents sequences that are values of mappings.
256    ///
257    /// By default (`true`) the dashes of such sequences are indented like
258    /// the keys of nested mappings (`key:\n  - a`).  With `false` they are at
259    /// the column of the key (`key:\n- a`), which is how libyaml, PyYAML and
260    /// `kubectl` write YAML.
261    pub const fn set_indent_sequences(&mut self, yes: bool) {
262        self.indent_sequences = yes;
263    }
264
265    /// Sets when collections are written in flow style.
266    ///
267    /// This has no effect with [`Indent::None`] where everything is written
268    /// in flow style.
269    ///
270    /// ```
271    /// use deser::Serialize;
272    /// use deser_yaml::{FlowPolicy, SerializerConfig};
273    ///
274    /// #[derive(Serialize)]
275    /// struct Config {
276    ///     ports: Vec<u16>,
277    ///     groups: Vec<Vec<u16>>,
278    /// }
279    ///
280    /// let config = Config {
281    ///     ports: vec![80, 443],
282    ///     groups: vec![vec![1], vec![2, 3]],
283    /// };
284    /// const FLOW: SerializerConfig =
285    ///     SerializerConfig::builder().flow(FlowPolicy::LeafIfFits(80)).build();
286    /// assert_eq!(
287    ///     FLOW.to_string(&config).unwrap(),
288    ///     "ports: [80, 443]\ngroups:\n  - [1]\n  - [2, 3]\n"
289    /// );
290    /// ```
291    pub const fn set_flow(&mut self, policy: FlowPolicy) {
292        self.flow = policy;
293    }
294
295    /// Folds long strings at the given width.
296    ///
297    /// Strings without line breaks that are longer than the width are
298    /// written as folded block scalars (`>`) with lines that do not exceed
299    /// the width if possible.  Strings are only folded if they read back
300    /// unchanged.  By default strings are not folded.  The width is also used
301    /// for strings with the [`Folded`](crate::style::Folded) hint (80 if not
302    /// set).
303    pub const fn set_fold_width(&mut self, width: Option<usize>) {
304        self.fold_width = width;
305    }
306
307    /// Sets how strings are quoted that cannot be written plain.
308    pub const fn set_quote_style(&mut self, style: QuoteStyle) {
309        self.quote_style = style;
310    }
311
312    /// Quotes all strings, including strings with line breaks.
313    pub const fn set_quote_all(&mut self, yes: bool) {
314        self.quote_all = yes;
315    }
316
317    /// Sets how strings with line breaks are written.
318    pub const fn set_multiline(&mut self, style: MultilineStyle) {
319        self.multiline = style;
320    }
321
322    /// Sets how null is written.
323    pub const fn set_null_style(&mut self, style: NullStyle) {
324        self.null_style = style;
325    }
326
327    /// Sets the oldest YAML version that readers of the output may use.
328    ///
329    /// Plain strings are quoted if a reader of this (or a later) version
330    /// would read them as something else than a string.  The default is
331    /// [`Version::V1_1`]: strings like `yes`, `on`, `0777`, `1:30` or
332    /// `2001-12-14` are quoted.  With [`Version::V1_2`] they are plain.
333    ///
334    /// ```
335    /// use deser_yaml::{SerializerConfig, Version};
336    ///
337    /// assert_eq!(deser_yaml::to_string(&"yes").unwrap(), "'yes'\n");
338    /// const V1_2: SerializerConfig =
339    ///     SerializerConfig::builder().compat(Version::V1_2).build();
340    /// assert_eq!(V1_2.to_string(&"yes").unwrap(), "yes\n");
341    /// ```
342    pub const fn set_compat(&mut self, version: Version) {
343        self.compat = version;
344    }
345
346    /// Writes bytes as `!!binary`.
347    ///
348    /// By default (`true`) YAML is a format with native bytes: bytes are
349    /// written as base64 with the `!!binary` tag, also bytes that request a
350    /// representation for formats without native bytes (see
351    /// [`BytesFallback`](deser_core::adapters::BytesFallback)).  With
352    /// `false` bytes are represented like in JSON: in the format they request
353    /// or the [`BytesFormat`] of the [`Context`](deser_core::Context).
354    ///
355    /// ```
356    /// use deser::adapters::Base64UrlNoPad;
357    /// use deser::{BytesFormat, Context};
358    /// use deser_yaml::SerializerConfig;
359    ///
360    /// assert_eq!(
361    ///     deser_yaml::to_string(&b"\xfb\xff").unwrap(),
362    ///     "!!binary +/8=\n"
363    /// );
364    /// let config = SerializerConfig::builder()
365    ///     .binary(false)
366    ///     .context(Context::with(BytesFormat::encoded::<Base64UrlNoPad>()))
367    ///     .build();
368    /// assert_eq!(config.to_string(&b"\xfb\xff").unwrap(), "-_8\n");
369    /// ```
370    ///
371    /// More encodings (such as hex) are provided by
372    /// [`deser-encoding`](https://docs.rs/deser-encoding).  Bytes in other
373    /// formats than base64 (or sequences) need to be deserialized with the
374    /// same format in the context.
375    pub const fn set_binary(&mut self, yes: bool) {
376        self.binary = yes;
377    }
378
379    /// Writes date-times with the `!!timestamp` tag.
380    ///
381    /// Dates and date-times with offset ([`Datetime`](deser_core::ext::Datetime))
382    /// are written as YAML timestamps.  By default they are plain which YAML
383    /// 1.1 readers resolve as timestamps and YAML 1.2 readers as strings
384    /// (which date / time types accept).  With the tag all readers resolve
385    /// them as timestamps.  Local date-times and times are always strings.
386    pub const fn set_timestamp_tag(&mut self, yes: bool) {
387        self.timestamp_tag = yes;
388    }
389
390    /// Always starts documents with `---`.
391    ///
392    /// When writing a stream of documents (see [`deser::io`](deser_core::io)), documents
393    /// after the first one always start with `---`.
394    pub const fn set_document_start(&mut self, yes: bool) {
395        self.document_start = yes;
396    }
397
398    /// Starts documents with a `%YAML 1.2` directive.
399    pub const fn set_version_directive(&mut self, yes: bool) {
400        self.version_directive = yes;
401    }
402
403    /// Ends documents with a document end marker (`...`).
404    ///
405    /// When a stream of documents is read (see `deser::io`), a document
406    /// is complete once the next document starts or once it's ended with
407    /// `...`.  For streams that stay open (like sockets) this allows the
408    /// reader to see the end of a document without waiting for the next
409    /// one.
410    ///
411    /// ```
412    /// use deser_yaml::{Serializer, SerializerConfig};
413    ///
414    /// const ENDED: SerializerConfig =
415    ///     SerializerConfig::builder().end_documents(true).build();
416    /// let mut serializer = Serializer::with_config(ENDED);
417    /// serializer.serialize(&"a").unwrap();
418    /// serializer.serialize(&"b").unwrap();
419    /// assert_eq!(serializer.finish(), "a\n...\n---\nb\n...\n");
420    /// ```
421    pub const fn set_end_documents(&mut self, yes: bool) {
422        self.end_documents = yes;
423    }
424
425    /// Creates the emitter of a document which writes into the output.
426    ///
427    /// This writes what precedes the document, `index` is the number of
428    /// documents written before.
429    pub(crate) fn emitter(&self, index: usize, mut out: String, bytes: BytesFormat) -> Emitter {
430        if self.version_directive {
431            // directives can only follow the end of a document
432            if index > 0 && !self.end_documents {
433                out.push_str("...\n");
434            }
435            out.push_str("%YAML 1.2\n---\n");
436        } else if self.document_start || index > 0 {
437            out.push_str("---\n");
438        }
439        Emitter::new(self, out, bytes)
440    }
441
442    /// Writes the end of a document once its value was written.
443    pub(crate) fn end_document(&self, emitter: &mut Emitter) -> Result<(), Error> {
444        emitter.finish()?;
445        if self.end_documents {
446            emitter.out.push_str("...\n");
447        }
448        Ok(())
449    }
450
451    /// Serializes the value of a driver as a document of a stream at once
452    /// and appends it to the output.
453    ///
454    /// Unlike `document_part` this does not refer to the pausable instance
455    /// of the driver which is only needed by stream serializers.  `index` is
456    /// the number of documents written before.  If this fails, what was
457    /// appended by the call is removed from the output.
458    pub(crate) fn document_whole(
459        &self,
460        index: usize,
461        driver: &mut SerializeDriver<'_>,
462        out: &mut String,
463    ) -> Result<(), Error> {
464        let len = out.len();
465        let mut emitter = self.emitter(index, std::mem::take(out), BytesFormat::of(driver.state()));
466        let rv = driver
467            .drive(|event, state| emitter.event(event, state))
468            .and_then(|()| self.end_document(&mut emitter));
469        *out = emitter.out;
470        if rv.is_err() {
471            out.truncate(len);
472        }
473        rv
474    }
475
476    /// Serializes (a part of) the value of a driver as a document of a
477    /// stream and appends it to the output.
478    ///
479    /// The progress of the document is kept in `document` (see
480    /// `StreamSerializer::drive_partial`), `true` is returned once the
481    /// document is complete.  `index` is the number of documents written
482    /// before.  If this fails, what was appended by the call is removed
483    /// from the output.
484    pub(crate) fn document_part(
485        &self,
486        index: usize,
487        document: &mut Option<Box<Emitter>>,
488        driver: &mut SerializeDriver<'_>,
489        out: &mut String,
490        limit: usize,
491    ) -> Result<bool, Error> {
492        // a document that is written at once is written into the output
493        // directly without boxing the emitter
494        if document.is_none() && limit == usize::MAX {
495            return self.document_whole(index, driver, out).map(|()| true);
496        }
497        let len = out.len();
498        // the emitter writes into an empty output directly, otherwise its
499        // output is appended
500        let adopt = out.is_empty();
501        let mut emitter = match document.take() {
502            Some(mut emitter) => {
503                if adopt {
504                    emitter.out = std::mem::take(out);
505                }
506                emitter
507            }
508            None => {
509                let buffer = match adopt {
510                    true => std::mem::take(out),
511                    false => String::new(),
512                };
513                Box::new(self.emitter(index, buffer, BytesFormat::of(driver.state())))
514            }
515        };
516        // after an error the document is abandoned, its emitter is dropped
517        emitter.limit = limit;
518        let rv = driver.drive_until(&mut *emitter).and_then(|done| {
519            if done {
520                self.end_document(&mut emitter)?;
521            }
522            Ok(done)
523        });
524        let done = match rv {
525            Ok(done) => done,
526            Err(err) => {
527                if adopt {
528                    *out = std::mem::take(&mut emitter.out);
529                }
530                out.truncate(len);
531                return Err(err);
532            }
533        };
534        let output = emitter.take_output();
535        if adopt {
536            *out = output;
537        } else {
538            out.push_str(&output);
539        }
540        if !done {
541            *document = Some(emitter);
542        }
543        Ok(done)
544    }
545
546    /// Serializes the given value.
547    pub fn to_string<T: Serialize + ?Sized>(&self, value: &T) -> Result<String, Error> {
548        self.to_string_ref(SerializeRef::new(&value))
549    }
550
551    /// Serializes the given value with a configured driver.
552    ///
553    /// The callback is invoked with the driver before the serialization
554    /// starts, for instance to add [`Layer`](deser_core::ser::Layer)s.
555    pub fn to_string_with<F, T: Serialize + ?Sized>(
556        &self,
557        value: &T,
558        setup: F,
559    ) -> Result<String, Error>
560    where
561        F: FnOnce(&mut SerializeDriver<'_>),
562    {
563        let mut driver = SerializeDriver::new(&value);
564        setup(&mut driver);
565        self.apply_context(&mut driver);
566        let mut out = String::new();
567        self.document_whole(0, &mut driver, &mut out)?;
568        Ok(out)
569    }
570
571    /// Serializes a value whose type is erased (see
572    /// [`to_string`](Self::to_string)).
573    ///
574    /// This is not generic: the code that exists for every type only
575    /// erases it.
576    fn to_string_ref(&self, value: SerializeRef<'_>) -> Result<String, Error> {
577        let mut driver = SerializeDriver::from_ref(value);
578        self.apply_context(&mut driver);
579        let mut out = String::new();
580        self.document_whole(0, &mut driver, &mut out)?;
581        Ok(out)
582    }
583}
584
585/// Builds a [`SerializerConfig`].
586///
587/// The methods have the names of the setters of [`SerializerConfig`] (without `set_`).
588#[derive(Debug, Clone)]
589#[must_use]
590pub struct SerializerConfigBuilder {
591    value: SerializerConfig,
592}
593
594impl SerializerConfigBuilder {
595    /// Creates a builder that starts with the default.
596    pub const fn new() -> SerializerConfigBuilder {
597        SerializerConfigBuilder {
598            value: SerializerConfig::new(),
599        }
600    }
601
602    /// Sets how the output is indented.
603    ///
604    /// See [`SerializerConfig::set_indent`].
605    pub const fn indent(mut self, indent: Indent) -> SerializerConfigBuilder {
606        self.value.set_indent(indent);
607        self
608    }
609
610    /// Indents sequences that are values of mappings.
611    ///
612    /// See [`SerializerConfig::set_indent_sequences`].
613    pub const fn indent_sequences(mut self, yes: bool) -> SerializerConfigBuilder {
614        self.value.set_indent_sequences(yes);
615        self
616    }
617
618    /// Sets when collections are written in flow style.
619    ///
620    /// See [`SerializerConfig::set_flow`].
621    pub const fn flow(mut self, policy: FlowPolicy) -> SerializerConfigBuilder {
622        self.value.set_flow(policy);
623        self
624    }
625
626    /// Folds long strings at the given width.
627    ///
628    /// See [`SerializerConfig::set_fold_width`].
629    pub const fn fold_width(mut self, width: Option<usize>) -> SerializerConfigBuilder {
630        self.value.set_fold_width(width);
631        self
632    }
633
634    /// Sets how strings are quoted that cannot be written plain.
635    ///
636    /// See [`SerializerConfig::set_quote_style`].
637    pub const fn quote_style(mut self, style: QuoteStyle) -> SerializerConfigBuilder {
638        self.value.set_quote_style(style);
639        self
640    }
641
642    /// Quotes all strings, including strings with line breaks.
643    ///
644    /// See [`SerializerConfig::set_quote_all`].
645    pub const fn quote_all(mut self, yes: bool) -> SerializerConfigBuilder {
646        self.value.set_quote_all(yes);
647        self
648    }
649
650    /// Sets how strings with line breaks are written.
651    ///
652    /// See [`SerializerConfig::set_multiline`].
653    pub const fn multiline(mut self, style: MultilineStyle) -> SerializerConfigBuilder {
654        self.value.set_multiline(style);
655        self
656    }
657
658    /// Sets how null is written.
659    ///
660    /// See [`SerializerConfig::set_null_style`].
661    pub const fn null_style(mut self, style: NullStyle) -> SerializerConfigBuilder {
662        self.value.set_null_style(style);
663        self
664    }
665
666    /// Sets the oldest YAML version that readers of the output may use.
667    ///
668    /// See [`SerializerConfig::set_compat`].
669    pub const fn compat(mut self, version: Version) -> SerializerConfigBuilder {
670        self.value.set_compat(version);
671        self
672    }
673
674    /// Writes bytes as `!!binary`.
675    ///
676    /// See [`SerializerConfig::set_binary`].
677    pub const fn binary(mut self, yes: bool) -> SerializerConfigBuilder {
678        self.value.set_binary(yes);
679        self
680    }
681
682    /// Writes date-times with the `!!timestamp` tag.
683    ///
684    /// See [`SerializerConfig::set_timestamp_tag`].
685    pub const fn timestamp_tag(mut self, yes: bool) -> SerializerConfigBuilder {
686        self.value.set_timestamp_tag(yes);
687        self
688    }
689
690    /// Always starts documents with `---`.
691    ///
692    /// See [`SerializerConfig::set_document_start`].
693    pub const fn document_start(mut self, yes: bool) -> SerializerConfigBuilder {
694        self.value.set_document_start(yes);
695        self
696    }
697
698    /// Starts documents with a `%YAML 1.2` directive.
699    ///
700    /// See [`SerializerConfig::set_version_directive`].
701    pub const fn version_directive(mut self, yes: bool) -> SerializerConfigBuilder {
702        self.value.set_version_directive(yes);
703        self
704    }
705
706    /// Ends documents with a document end marker (`...`).
707    ///
708    /// See [`SerializerConfig::set_end_documents`].
709    pub const fn end_documents(mut self, yes: bool) -> SerializerConfigBuilder {
710        self.value.set_end_documents(yes);
711        self
712    }
713
714    /// Sets the context the values are serialized in.
715    ///
716    /// See [`SerializerConfig::set_context`].
717    pub fn context(mut self, context: deser_core::Context) -> SerializerConfigBuilder {
718        self.value.set_context(context);
719        self
720    }
721
722    /// Returns the built [`SerializerConfig`].
723    pub const fn build(self) -> SerializerConfig {
724        // the value cannot be moved out of the builder in a const fn as the
725        // builder needs dropping (the context has a destructor)
726        // SAFETY: the value is read once and the builder is forgotten
727        let value = unsafe { core::ptr::read(&self.value) };
728        core::mem::forget(self);
729        value
730    }
731}
732
733impl Default for SerializerConfigBuilder {
734    fn default() -> SerializerConfigBuilder {
735        SerializerConfigBuilder::new()
736    }
737}
738
739/// Serializes values into YAML documents.
740///
741/// Every call to [`serialize`](Self::serialize) writes a document,
742/// documents after the first start with `---`.
743///
744/// ```
745/// use deser_yaml::Serializer;
746///
747/// let mut serializer = Serializer::new();
748/// serializer.serialize(&"a").unwrap();
749/// serializer.serialize(&vec![1, 2]).unwrap();
750/// assert_eq!(serializer.finish(), "a\n---\n- 1\n- 2\n");
751/// ```
752///
753/// The serializer is also the stream serializer of YAML (see
754/// [`StreamSerializer`](ser::StreamSerializer)): the output can be taken
755/// while documents are written, and large documents can be written in
756/// parts.  To write to a [`Write`](std::io::Write) use
757/// [`SerializerConfig::writer`].
758pub struct Serializer {
759    config: SerializerConfig,
760    out: String,
761    written: usize,
762    // the document that is written in parts
763    document: Option<Box<Emitter>>,
764    // a document was started with `drive_partial` and is not complete
765    in_progress: bool,
766}
767
768impl Default for Serializer {
769    fn default() -> Serializer {
770        Serializer::new()
771    }
772}
773
774impl Clone for Serializer {
775    /// Clones the serializer.
776    ///
777    /// The clone of a serializer that writes a document in parts cannot
778    /// write more documents (see
779    /// [`StreamSerializer::in_progress`](ser::StreamSerializer::in_progress)).
780    fn clone(&self) -> Serializer {
781        Serializer {
782            config: self.config.clone(),
783            out: self.out.clone(),
784            written: self.written,
785            document: None,
786            in_progress: self.in_progress,
787        }
788    }
789}
790
791impl std::fmt::Debug for Serializer {
792    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
793        f.debug_struct("Serializer")
794            .field("config", &self.config)
795            .field("output", &self.out)
796            .field("written", &self.written)
797            .field("in_progress", &self.in_progress)
798            .finish()
799    }
800}
801
802impl Serializer {
803    /// Creates a serializer.
804    pub fn new() -> Serializer {
805        Serializer::with_config(SerializerConfig::new())
806    }
807
808    /// Creates a serializer with the given configuration.
809    pub fn with_config(config: SerializerConfig) -> Serializer {
810        Serializer::with_written(config, 0)
811    }
812
813    /// Creates a serializer for a stream that continues after the given
814    /// number of documents.
815    ///
816    /// This is useful to append to a stream that was written before: the
817    /// next document starts with `---`.
818    pub fn with_written(config: SerializerConfig, written: usize) -> Serializer {
819        Serializer {
820            config,
821            out: String::new(),
822            written,
823            document: None,
824            in_progress: false,
825        }
826    }
827
828    /// Returns the configuration.
829    pub fn config(&self) -> &SerializerConfig {
830        &self.config
831    }
832
833    /// Returns the number of documents that were written.
834    pub fn written(&self) -> usize {
835        self.written
836    }
837
838    /// Serializes a value.
839    ///
840    /// If the value fails to serialize, nothing is written.
841    pub fn serialize<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
842        ser::Serializer::serialize(self, value)
843    }
844
845    /// Serializes a value with a configured driver.
846    ///
847    /// The callback is invoked with the driver before the value is
848    /// serialized, for instance to add [`Layer`](deser_core::ser::Layer)s.
849    pub fn serialize_with<F, T: Serialize + ?Sized>(
850        &mut self,
851        value: &T,
852        setup: F,
853    ) -> Result<(), Error>
854    where
855        F: FnOnce(&mut SerializeDriver<'_>),
856    {
857        ser::Serializer::serialize_with(self, value, setup)
858    }
859
860    /// Returns the documents written so far (that were not cleared).
861    pub fn as_str(&self) -> &str {
862        &self.out
863    }
864
865    /// Returns the documents.
866    pub fn finish(self) -> String {
867        self.out
868    }
869}
870
871impl ser::Serializer for Serializer {
872    fn drive(&mut self, driver: &mut SerializeDriver<'_>) -> Result<(), Error> {
873        // only `drive_partial` continues a document
874        if self.in_progress {
875            return Err(Error::in_progress());
876        }
877        ser::StreamSerializer::drive_partial(self, driver, usize::MAX).map(|_| ())
878    }
879}
880
881impl ser::StreamSerializer for Serializer {
882    fn output(&self) -> &[u8] {
883        self.out.as_bytes()
884    }
885
886    fn clear_output(&mut self) {
887        self.out.clear();
888    }
889
890    fn supports_partial(&self) -> bool {
891        true
892    }
893
894    fn drive_partial(
895        &mut self,
896        driver: &mut SerializeDriver<'_>,
897        limit: usize,
898    ) -> Result<bool, Error> {
899        if !self.config.context.is_empty() {
900            driver.set_default_context(self.config.context.clone());
901        }
902        if self.document.is_none() && self.in_progress {
903            return Err(Error::in_progress());
904        }
905        // the parts of a document that failed stay written (see
906        // `in_progress`)
907        if !self.config.document_part(
908            self.written,
909            &mut self.document,
910            driver,
911            &mut self.out,
912            limit,
913        )? {
914            self.in_progress = true;
915            return Ok(false);
916        }
917        self.in_progress = false;
918        self.written += 1;
919        Ok(true)
920    }
921
922    fn in_progress(&self) -> bool {
923        self.in_progress
924    }
925}
926
927#[cfg(feature = "io")]
928impl SerializerConfig {
929    /// Creates a writer of YAML documents (see
930    /// [`deser::io::Writer`](deser_core::io::Writer)).
931    ///
932    /// Every value is written as a document, documents after the first
933    /// start with `---`.  The output of large documents is written in parts
934    /// while they are serialized.
935    ///
936    /// ```
937    /// use deser_yaml::SerializerConfig;
938    ///
939    /// let mut writer = SerializerConfig::new().writer(Vec::new());
940    /// writer.write(&"a").unwrap();
941    /// writer.write(&vec![1, 2]).unwrap();
942    /// assert_eq!(writer.into_inner(), b"a\n---\n- 1\n- 2\n");
943    /// ```
944    pub fn writer<W: std::io::Write>(&self, writer: W) -> deser_core::io::Writer<W, Serializer> {
945        deser_core::io::Writer::new(writer, Serializer::with_config(self.clone()))
946    }
947
948    /// Serializes a value to a writer.
949    ///
950    /// See [`to_writer`].
951    pub fn to_writer<W: std::io::Write, T: Serialize + ?Sized>(
952        &self,
953        writer: W,
954        value: &T,
955    ) -> Result<(), Error> {
956        self.writer(writer).write(value)
957    }
958}
959
960/// Serializes a value to a writer.
961///
962/// The output of large documents is written in parts while they are
963/// serialized (see [`deser::io`](deser_core::io)), the writer does not
964/// need to be buffered.
965///
966/// ```
967/// let mut out = Vec::new();
968/// deser_yaml::to_writer(&mut out, &vec![1, 2]).unwrap();
969/// assert_eq!(out, b"- 1\n- 2\n");
970/// ```
971#[cfg(feature = "io")]
972pub fn to_writer<W: std::io::Write, T: Serialize + ?Sized>(
973    writer: W,
974    value: &T,
975) -> Result<(), Error> {
976    SerializerConfig::new().to_writer(writer, value)
977}
978
979/// Serializes a value to YAML.
980///
981/// This uses the default [`SerializerConfig`], see there for more
982/// information.
983///
984/// ```
985/// use deser::Serialize;
986///
987/// #[derive(Serialize)]
988/// struct Service {
989///     image: String,
990///     ports: Vec<u16>,
991///     command: Option<String>,
992/// }
993///
994/// let service = Service {
995///     image: "nginx".into(),
996///     ports: vec![80, 443],
997///     command: None,
998/// };
999/// assert_eq!(
1000///     deser_yaml::to_string(&service).unwrap(),
1001///     "image: nginx\nports:\n  - 80\n  - 443\ncommand: null\n"
1002/// );
1003/// ```
1004pub fn to_string<T: Serialize + ?Sized>(value: &T) -> Result<String, Error> {
1005    SerializerConfig::new().to_string(value)
1006}