serde_saphyr/serializer_options.rs
1//! Serializer options for YAML emission.
2//!
3//! Controls indentation and optional anchor name generation for the serializer.
4//!
5//! Example: use 4-space indentation and a custom anchor naming scheme.
6//!
7//! ```rust
8//! use serde::Serialize;
9//!
10//! #[derive(Serialize)]
11//! struct Item { a: i32, b: bool }
12//!
13//! let mut buf = String::new();
14//! let opts = serde_saphyr::ser_options! {
15//! indent_step: 4,
16//! anchor_generator: Some(|id| format!("id{}/", id)),
17//! };
18//! serde_saphyr::to_fmt_writer_with_options(&mut buf, &Item { a: 1, b: true }, opts).unwrap();
19//! assert!(buf.contains("a: 1"));
20//! ```
21
22use crate::ser_error::Error;
23
24/// Serializer options for YAML emission.
25///
26/// This struct controls various aspects of YAML serialization, such as indentation,
27/// anchor generation, and scalar styles.
28///
29/// Construct `SerializerOptions` using the [`ser_options!`](crate::ser_options!)
30/// macro to ensure compatibility with future updates.
31///
32/// ```rust
33/// use serde::Serialize;
34/// use serde_saphyr::{ser_options, to_string_with_options};
35///
36/// #[derive(Serialize)]
37/// struct Config {
38/// name: String,
39/// values: Vec<i32>,
40/// }
41///
42/// let config = Config {
43/// name: "test".to_string(),
44/// values: vec![1, 2, 3],
45/// };
46///
47/// // Use 4-space indentation and quote all strings
48/// let options = ser_options! {
49/// indent_step: 4,
50/// quote_all: true,
51/// };
52///
53/// let yaml = to_string_with_options(&config, options).unwrap();
54/// ```
55#[derive(Clone, Copy)]
56pub struct SerializerOptions {
57 /// If true, empty maps are emitted as braces {} and empty lists as [] (this is the default).
58 /// Such form is equally valid YAML, allows to tell empty from null and may be easier for a
59 /// human to grasp.
60 pub empty_as_braces: bool,
61 /// Number of spaces to indent per nesting level when emitting block-style collections (2 by default).
62 /// 0 value is invalid and will result and error when trying to deserialize, because
63 /// no indentation would produce invalid YAML otherwise.
64 pub indent_step: usize,
65 /// Optional custom anchor-name generator.
66 ///
67 /// Receives a monotonically increasing `usize` id (starting at 1) and returns the
68 /// anchor name to emit. If `None`, the built-in generator yields names like `a1`, `a2`, ...
69 pub anchor_generator: Option<fn(usize) -> String>,
70 /// Threshold for block-string wrappers ([crate::LitStr]/[crate::FoldStr] and owned variants
71 /// [crate::LitString]/[crate::FoldString]).
72 ///
73 /// If the string contains a newline, block style is always used. Otherwise, when the
74 /// string is single-line and its length is strictly less than this threshold, the
75 /// serializer emits a normal YAML scalar (no block style). Longer strings use block
76 /// styles `|` or `>` depending on the wrapper. See the type docs for
77 /// [crate::LitStr], [crate::FoldStr], [crate::LitString] and [crate::FoldString] for
78 /// examples.
79 pub min_fold_chars: usize,
80 /// Maximum width (in characters) for lines in folded block scalars (`>`).
81 ///
82 /// Lines are wrapped **only** at whitespace so that each emitted line is at most
83 /// this many characters long (excluding indentation). If no whitespace is present
84 /// within the limit (e.g., a single long token), the line is emitted unwrapped
85 /// to preserve round-trip correctness: YAML folded scalars typically fold inserted
86 /// newlines back as spaces when parsing. 32 default.
87 pub folded_wrap_chars: usize,
88 /// When enabled, serialize simple enums that become a single scalar (unit variants)
89 /// using YAML tags, e.g. `!!Enum Variant` instead of a plain scalar `Variant`.
90 /// Deserializer does not need this setting as both cases will be understood. Off by default.
91 pub tagged_enums: bool,
92
93 /// When enabled, strings containing more than folded_wrap_chars (80 by default) are written
94 /// in wrapped multistring folded form (>), and strings containing new lines are written in
95 /// literal form (|), selecting format depending on the number of empty lines at the end.
96 /// On by default.
97 pub prefer_block_scalars: bool,
98
99 /// When enabled, quote all string scalars. Uses single quotes by default,
100 /// but switches to double quotes when the string contains escape sequences
101 /// (control characters like `\n`, `\t`, `\r`, backslash) or single quotes.
102 /// Disables block scalar styles (`|` and `>`) for quoted strings when active.
103 /// Off by default.
104 pub quote_all: bool,
105
106 /// When enabled, emit `%YAML 1.2` at the beginning of the document and
107 /// use YAML 1.2 rules for certain compatibility heuristics.
108 ///
109 /// In particular, YAML 1.1 boolean spellings like `yes`/`no`/`on`/`off`/`y`/`n`
110 /// will **not** be treated as booleans for the purpose of auto-quoting. In cases
111 /// like multiple x, y coordinates quoting y may be very annoying.
112 /// Default: false.
113 pub yaml_12: bool,
114}
115
116// Below this length, block-string wrappers serialize as regular scalars
117// instead of YAML block styles. This keeps short values compact.
118pub(crate) const MIN_FOLD_CHARS: usize = 32;
119/// Maximum width (in characters) for lines inside folded block scalars.
120/// Lines will be wrapped at whitespace so that each emitted line is at most
121/// this many characters long (excluding indentation). If no whitespace is
122/// available within the limit, the line is not wrapped.
123pub(crate) const FOLDED_WRAP_CHARS: usize = 80;
124
125impl SerializerOptions {
126 #[allow(deprecated)]
127 pub(crate) fn consistent(&self) -> Result<(), Error> {
128 if self.indent_step == 0 {
129 return Err(Error::InvalidOptions(
130 "Invalid indent step must be positive".to_string(),
131 ));
132 }
133 Ok(())
134 }
135}
136
137impl Default for SerializerOptions {
138 #[allow(deprecated)]
139 fn default() -> Self {
140 // Defaults mirror internal constants used by the serializer.
141 Self {
142 indent_step: 2,
143 anchor_generator: None,
144 min_fold_chars: MIN_FOLD_CHARS,
145 folded_wrap_chars: FOLDED_WRAP_CHARS,
146 tagged_enums: false,
147 empty_as_braces: true,
148 prefer_block_scalars: true,
149 quote_all: false,
150 yaml_12: false,
151 }
152 }
153}