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}