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}