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