serde_saphyr/long_strings.rs
1use serde::de::{Deserialize, Deserializer};
2use serde::ser::{Serialize, Serializer};
3use std::ops::Deref;
4
5// Flow hints and block-string hints: we use newtype-struct names.
6// These are consumed by the YAML serializer (see `ser.rs`) via Serde's data model.
7pub(crate) const NAME_LIT_STR: &str = "__yaml_lit_str";
8pub(crate) const NAME_FOLD_STR: &str = "__yaml_fold_str";
9
10/// Force a YAML block literal string using the `|` style.
11///
12/// Emits the inner `&str` as a block scalar that preserves newlines exactly
13/// as written. Each line is indented one level deeper than the surrounding
14/// indentation where the value appears.
15///
16/// In short: use [LitStr] (|) to preserve line breaks exactly; use [FoldStr] (>) when you want
17/// readers to display line breaks as spaces (soft-wrapped paragraphs).
18///
19/// See also: [FoldStr], [LitString], [FoldString].
20///
21/// Behavior
22/// - Uses YAML's literal block style: a leading `|` followed by newline.
23/// - Newlines are preserved verbatim by YAML consumers.
24/// - Indentation is handled automatically by the serializer.
25/// - Works in mapping values, sequence items, and at the top level.
26///
27/// Examples
28///
29/// Top-level literal block string:
30/// ```rust
31/// # #[cfg(feature = "serialize")]
32/// # {
33/// let long = "line 1\nline 2\n".repeat(20);
34/// let out = serde_saphyr::to_string(&serde_saphyr::LitStr(&long)).unwrap();
35/// assert!(out.starts_with("|\n "));
36/// # }
37/// ```
38///
39/// As a mapping value:
40/// ```rust
41/// # #[cfg(feature = "serialize")]
42/// # {
43/// use serde::Serialize;
44/// #[derive(Serialize)]
45/// struct S { note: serde_saphyr::LitStr<'static> }
46/// let s = S { note: serde_saphyr::LitStr("a\nb") };
47/// let out = serde_saphyr::to_string(&s).unwrap();
48/// assert_eq!(out, "note: |-\n a\n b\n");
49/// # }
50/// ```
51#[derive(Clone, Copy)]
52pub struct LitStr<'a>(pub &'a str);
53
54/// Owned-string variant of [LitStr] that forces a YAML block literal string using the `|` style.
55///
56/// This works the same as [LitStr] but takes ownership of a String. Useful when you already
57/// have an owned String and want to avoid borrowing lifetimes.
58///
59/// See also: [FoldStr], [FoldString].
60///
61/// Example
62/// ```rust
63/// # #[cfg(feature = "serialize")]
64/// # {
65/// let out = serde_saphyr::to_string(&serde_saphyr::LitString("line 1\nline 2".to_string())).unwrap();
66/// assert_eq!(out, "|-\n line 1\n line 2\n");
67/// # }
68/// ```
69#[derive(Clone, Debug, PartialEq, Eq)]
70pub struct LitString(pub String);
71
72/// Force a YAML folded block string using the `>` style.
73///
74/// Emits the inner `&str` as a block scalar that suggests folding line breaks
75/// to spaces for display by YAML consumers (empty lines are kept as paragraph
76/// breaks). The serializer writes each line on its own; the folding behavior is
77/// applied by consumers of the YAML, not during serialization.
78///
79/// In short: use [FoldStr] (>) for human-readable paragraphs that may soft-wrap; use
80/// [LitStr] (|) when you need to preserve line breaks exactly as written.
81///
82/// See also: [LitStr], [LitString], [FoldString].
83///
84/// Behavior
85/// - Uses YAML's folded block style: a leading `>` followed by newline.
86/// - Intended for human-readable paragraphs where soft-wrapping is desirable.
87/// - Indentation is handled automatically by the serializer.
88/// - Works in mapping values, sequence items, and at the top level.
89///
90/// Examples
91///
92/// Top-level folded block string:
93/// ```rust
94/// # #[cfg(feature = "serialize")]
95/// # {
96/// let out = serde_saphyr::to_string(&serde_saphyr::FoldStr("line 1\nline 2")).unwrap();
97/// assert_eq!(out, ">\n line 1\n line 2\n");
98/// # }
99/// ```
100///
101/// As a mapping value:
102/// ```rust
103/// # #[cfg(feature = "serialize")]
104/// # {
105/// use serde::Serialize;
106/// #[derive(Serialize)]
107/// struct S { note: serde_saphyr::FoldStr<'static> }
108/// let s = S { note: serde_saphyr::FoldStr("a\nb") };
109/// let out = serde_saphyr::to_string(&s).unwrap();
110/// assert_eq!(out, "note: >\n a\n b\n");
111/// # }
112/// ```
113#[derive(Clone, Copy)]
114pub struct FoldStr<'a>(pub &'a str);
115
116/// Owned-string variant of [FoldStr] that forces a YAML folded block string using the `>` style.
117///
118/// Same behavior as [FoldStr] but owns a String.
119///
120/// See also: [LitStr], [LitString].
121///
122/// Example
123/// ```rust
124/// # #[cfg(feature = "serialize")]
125/// # {
126/// let out = serde_saphyr::to_string(&serde_saphyr::FoldString("line 1\nline 2".to_string())).unwrap();
127/// assert_eq!(out, ">\n line 1\n line 2\n");
128/// # }
129/// ```
130#[derive(Clone, Debug, PartialEq, Eq)]
131pub struct FoldString(pub String);
132
133impl<'a> Serialize for LitStr<'a> {
134 fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
135 // Always delegate decision to the YAML serializer so it can apply options.
136 s.serialize_newtype_struct(NAME_LIT_STR, &self.0)
137 }
138}
139impl Serialize for LitString {
140 fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
141 s.serialize_newtype_struct(NAME_LIT_STR, &self.0)
142 }
143}
144impl<'a> Serialize for FoldStr<'a> {
145 fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
146 s.serialize_newtype_struct(NAME_FOLD_STR, &self.0)
147 }
148}
149impl Serialize for FoldString {
150 fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
151 s.serialize_newtype_struct(NAME_FOLD_STR, &self.0)
152 }
153}
154
155// Deserialization for owned block string wrappers: delegate to String
156impl<'de> Deserialize<'de> for LitString {
157 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
158 String::deserialize(deserializer).map(LitString)
159 }
160}
161impl<'de> Deserialize<'de> for FoldString {
162 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
163 String::deserialize(deserializer).map(FoldString)
164 }
165}
166
167// ------------------------------------------------------------
168// Convenience conversions / comparisons for block-string wrappers
169// ------------------------------------------------------------
170
171impl<'a> From<&'a str> for LitStr<'a> {
172 fn from(s: &'a str) -> Self {
173 LitStr(s)
174 }
175}
176
177impl<'a> From<&'a str> for FoldStr<'a> {
178 fn from(s: &'a str) -> Self {
179 FoldStr(s)
180 }
181}
182
183impl From<String> for LitString {
184 fn from(s: String) -> Self {
185 LitString(s)
186 }
187}
188
189impl From<String> for FoldString {
190 fn from(s: String) -> Self {
191 FoldString(s)
192 }
193}
194
195impl Deref for LitString {
196 type Target = str;
197 fn deref(&self) -> &Self::Target {
198 &self.0
199 }
200}
201
202impl Deref for FoldString {
203 type Target = str;
204 fn deref(&self) -> &Self::Target {
205 &self.0
206 }
207}
208
209impl<'a> Deref for LitStr<'a> {
210 type Target = str;
211 fn deref(&self) -> &Self::Target {
212 self.0
213 }
214}
215
216impl<'a> Deref for FoldStr<'a> {
217 type Target = str;
218 fn deref(&self) -> &Self::Target {
219 self.0
220 }
221}
222
223impl LitString {
224 pub fn into_inner(self) -> String {
225 self.0
226 }
227}
228
229impl FoldString {
230 pub fn into_inner(self) -> String {
231 self.0
232 }
233}
234
235impl PartialEq<FoldString> for LitString {
236 fn eq(&self, other: &FoldString) -> bool {
237 self.0 == other.0
238 }
239}
240
241impl PartialEq<LitString> for FoldString {
242 fn eq(&self, other: &LitString) -> bool {
243 self.0 == other.0
244 }
245}
246
247impl PartialEq<String> for LitString {
248 fn eq(&self, other: &String) -> bool {
249 &self.0 == other
250 }
251}
252
253impl PartialEq<String> for FoldString {
254 fn eq(&self, other: &String) -> bool {
255 &self.0 == other
256 }
257}
258
259impl PartialEq<&str> for LitString {
260 fn eq(&self, other: &&str) -> bool {
261 self.0 == *other
262 }
263}
264
265impl PartialEq<&str> for FoldString {
266 fn eq(&self, other: &&str) -> bool {
267 self.0 == *other
268 }
269}
270
271impl PartialEq<str> for LitString {
272 fn eq(&self, other: &str) -> bool {
273 self.0 == other
274 }
275}
276
277impl PartialEq<str> for FoldString {
278 fn eq(&self, other: &str) -> bool {
279 self.0 == other
280 }
281}