Skip to main content

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}