Skip to main content

serde_structprop/
ser.rs

1//! Serde [`Serializer`](serde::Serializer) for the structprop format.
2//!
3//! The public entry point is [`to_string`].  Internally a `Serializer` walks
4//! the serde data model and writes structprop text directly into an output
5//! [`String`].
6//!
7//! # Type mapping
8//!
9//! | Rust / serde | Structprop output |
10//! |---|---|
11//! | `bool` | `true` or `false` scalar |
12//! | integer / float | numeric scalar |
13//! | `String` / `&str` | bare scalar or `"quoted"` if it contains special chars |
14//! | `None` / `()` | `null` scalar |
15//! | struct / map | `key { … }` block at the current indentation level |
16//! | `Vec<T>` / sequence | `= { … }` inline list |
17//! | unit enum variant | bare variant name scalar |
18//! | newtype / tuple / struct enum variant | `variant_name { … }` block |
19
20use std::fmt::Write as FmtWrite;
21
22use crate::error::{Error, Result};
23use serde::ser::{self, Serialize};
24
25// ---------------------------------------------------------------------------
26// Public entry point
27// ---------------------------------------------------------------------------
28
29/// Serialize `value` into a structprop-formatted [`String`].
30///
31/// Top-level structs and maps produce a flat sequence of `key = value` /
32/// `key { … }` entries with no enclosing braces.  Sequences produce a bare
33/// `{\n … \n}` block.
34///
35/// # Errors
36///
37/// Returns [`Error::UnsupportedType`] if `T` contains raw byte slices, or
38/// [`Error::Message`] for any other serde-level serialization error.
39///
40/// # Examples
41///
42/// ```
43/// use serde::Serialize;
44/// use serde_structprop::to_string;
45///
46/// #[derive(Serialize)]
47/// struct Cfg { host: String, port: u16 }
48///
49/// let s = to_string(&Cfg { host: "localhost".into(), port: 8080 }).unwrap();
50/// assert!(s.contains("host = localhost"));
51/// assert!(s.contains("port = 8080"));
52/// ```
53pub fn to_string<T: Serialize>(value: &T) -> Result<String> {
54    let mut ser = Serializer::new(0);
55    value.serialize(&mut ser)?;
56    Ok(ser.output)
57}
58
59// ---------------------------------------------------------------------------
60// Serializer
61// ---------------------------------------------------------------------------
62
63/// Core serializer that accumulates structprop text in `output`.
64///
65/// `indent` is the current nesting depth; each level adds two spaces of
66/// leading whitespace to each emitted line.
67pub struct Serializer {
68    /// The accumulated output string.
69    pub(crate) output: String,
70    /// Current indentation level in spaces.
71    indent: usize,
72}
73
74impl Serializer {
75    /// Create a new [`Serializer`] starting at `indent` spaces of indentation.
76    fn new(indent: usize) -> Self {
77        Serializer {
78            output: String::new(),
79            indent,
80        }
81    }
82
83    /// Return a string of `self.indent` spaces.
84    fn pad(&self) -> String {
85        " ".repeat(self.indent)
86    }
87}
88
89// ---------------------------------------------------------------------------
90// Helpers
91// ---------------------------------------------------------------------------
92
93/// Wrap `s` in double quotes if it contains any structprop special characters
94/// (space, tab, newline, carriage return, `#`, `{`, `}`, or `=`); otherwise
95/// return it unchanged.
96///
97/// The structprop format has no escape sequences. A `"` character can appear
98/// inside a *bare* (unquoted) term, but not inside a *quoted* term. Therefore,
99/// strings that both require quoting (contain a special character) and contain
100/// a literal `"` will serialize to syntactically ambiguous output. Such strings
101/// cannot round-trip through this format.
102fn escape(s: &str) -> String {
103    if s.chars()
104        .any(|c| matches!(c, ' ' | '\t' | '\n' | '\r' | '#' | '{' | '}' | '='))
105    {
106        format!("\"{s}\"")
107    } else {
108        s.to_owned()
109    }
110}
111
112// ---------------------------------------------------------------------------
113// serde::Serializer impl
114// ---------------------------------------------------------------------------
115
116impl<'a> ser::Serializer for &'a mut Serializer {
117    type Ok = ();
118    type Error = Error;
119
120    /// Compound serializer types.
121    type SerializeSeq = SeqSerializer<'a>;
122    /// Compound serializer types.
123    type SerializeTuple = SeqSerializer<'a>;
124    /// Compound serializer types.
125    type SerializeTupleStruct = SeqSerializer<'a>;
126    /// Compound serializer types.
127    type SerializeTupleVariant = SeqSerializer<'a>;
128    /// Compound serializer types.
129    type SerializeMap = MapSerializer<'a>;
130    /// Compound serializer types.
131    type SerializeStruct = MapSerializer<'a>;
132    /// Compound serializer types.
133    type SerializeStructVariant = MapSerializer<'a>;
134
135    fn serialize_bool(self, v: bool) -> Result<()> {
136        self.output.push_str(if v { "true" } else { "false" });
137        Ok(())
138    }
139
140    fn serialize_i8(self, v: i8) -> Result<()> {
141        write!(self.output, "{v}").map_err(|e| Error::Message(e.to_string()))
142    }
143
144    fn serialize_i16(self, v: i16) -> Result<()> {
145        write!(self.output, "{v}").map_err(|e| Error::Message(e.to_string()))
146    }
147
148    fn serialize_i32(self, v: i32) -> Result<()> {
149        write!(self.output, "{v}").map_err(|e| Error::Message(e.to_string()))
150    }
151
152    fn serialize_i64(self, v: i64) -> Result<()> {
153        write!(self.output, "{v}").map_err(|e| Error::Message(e.to_string()))
154    }
155
156    fn serialize_u8(self, v: u8) -> Result<()> {
157        write!(self.output, "{v}").map_err(|e| Error::Message(e.to_string()))
158    }
159
160    fn serialize_u16(self, v: u16) -> Result<()> {
161        write!(self.output, "{v}").map_err(|e| Error::Message(e.to_string()))
162    }
163
164    fn serialize_u32(self, v: u32) -> Result<()> {
165        write!(self.output, "{v}").map_err(|e| Error::Message(e.to_string()))
166    }
167
168    fn serialize_u64(self, v: u64) -> Result<()> {
169        write!(self.output, "{v}").map_err(|e| Error::Message(e.to_string()))
170    }
171
172    fn serialize_f32(self, v: f32) -> Result<()> {
173        write!(self.output, "{v}").map_err(|e| Error::Message(e.to_string()))
174    }
175
176    fn serialize_f64(self, v: f64) -> Result<()> {
177        write!(self.output, "{v}").map_err(|e| Error::Message(e.to_string()))
178    }
179
180    fn serialize_char(self, v: char) -> Result<()> {
181        // Route through escape() so special characters are quoted.
182        self.output.push_str(&escape(&v.to_string()));
183        Ok(())
184    }
185
186    fn serialize_str(self, v: &str) -> Result<()> {
187        self.output.push_str(&escape(v));
188        Ok(())
189    }
190
191    fn serialize_bytes(self, _v: &[u8]) -> Result<()> {
192        Err(Error::UnsupportedType("bytes"))
193    }
194
195    fn serialize_none(self) -> Result<()> {
196        self.output.push_str("null");
197        Ok(())
198    }
199
200    fn serialize_some<T: Serialize + ?Sized>(self, value: &T) -> Result<()> {
201        value.serialize(self)
202    }
203
204    fn serialize_unit(self) -> Result<()> {
205        self.output.push_str("null");
206        Ok(())
207    }
208
209    fn serialize_unit_struct(self, _name: &'static str) -> Result<()> {
210        self.serialize_unit()
211    }
212
213    fn serialize_unit_variant(
214        self,
215        _name: &'static str,
216        _index: u32,
217        variant: &'static str,
218    ) -> Result<()> {
219        self.serialize_str(variant)
220    }
221
222    fn serialize_newtype_struct<T: Serialize + ?Sized>(
223        self,
224        _name: &'static str,
225        value: &T,
226    ) -> Result<()> {
227        value.serialize(self)
228    }
229
230    fn serialize_newtype_variant<T: Serialize + ?Sized>(
231        self,
232        _name: &'static str,
233        _index: u32,
234        variant: &'static str,
235        value: &T,
236    ) -> Result<()> {
237        // Encode as `variant = <payload>` (scalar) or `variant { … }` (object block)
238        // so the parser produces Object({"variant": payload}), which is exactly what
239        // deserialize_enum expects for newtype variants.
240        let mut ms = MapSerializer {
241            ser: self,
242            current_key: None,
243            variant_name: None,
244        };
245        ms.write_kv(variant, value)
246    }
247
248    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
249        Ok(SeqSerializer {
250            parent: self,
251            items: Vec::new(),
252            variant: None,
253        })
254    }
255
256    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
257        self.serialize_seq(Some(len))
258    }
259
260    fn serialize_tuple_struct(
261        self,
262        _name: &'static str,
263        len: usize,
264    ) -> Result<Self::SerializeTupleStruct> {
265        self.serialize_seq(Some(len))
266    }
267
268    fn serialize_tuple_variant(
269        self,
270        _name: &'static str,
271        _index: u32,
272        variant: &'static str,
273        _len: usize,
274    ) -> Result<Self::SerializeTupleVariant> {
275        Ok(SeqSerializer {
276            parent: self,
277            items: Vec::new(),
278            variant: Some(variant.to_owned()),
279        })
280    }
281
282    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
283        Ok(MapSerializer {
284            ser: self,
285            current_key: None,
286            variant_name: None,
287        })
288    }
289
290    fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
291        self.serialize_map(Some(len))
292    }
293
294    fn serialize_struct_variant(
295        self,
296        _name: &'static str,
297        _index: u32,
298        variant: &'static str,
299        _len: usize,
300    ) -> Result<Self::SerializeStructVariant> {
301        Ok(MapSerializer {
302            ser: self,
303            current_key: None,
304            variant_name: Some(variant.to_owned()),
305        })
306    }
307}
308
309// ---------------------------------------------------------------------------
310// SeqSerializer – handles sequences / arrays
311// ---------------------------------------------------------------------------
312
313/// [`ser::SerializeSeq`] (and related tuple impls) that collects rendered items
314/// then emits them as a `{ … }` block.
315pub struct SeqSerializer<'a> {
316    parent: &'a mut Serializer,
317    /// Each element serialized to a string, accumulated for deferred emission.
318    items: Vec<String>,
319    /// Set for tuple variants: the variant name to wrap the array under.
320    variant: Option<String>,
321}
322
323impl ser::SerializeSeq for SeqSerializer<'_> {
324    type Ok = ();
325    type Error = Error;
326
327    fn serialize_element<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<()> {
328        let mut inner = Serializer::new(0);
329        value.serialize(&mut inner)?;
330        self.items.push(inner.output);
331        Ok(())
332    }
333
334    fn end(self) -> Result<()> {
335        let pad = self.parent.pad();
336        let inner_pad = " ".repeat(self.parent.indent + 2);
337        writeln!(self.parent.output, "{{").map_err(|e| Error::Message(e.to_string()))?;
338        for item in &self.items {
339            writeln!(self.parent.output, "{inner_pad}{item}")
340                .map_err(|e| Error::Message(e.to_string()))?;
341        }
342        writeln!(self.parent.output, "{pad}}}").map_err(|e| Error::Message(e.to_string()))
343    }
344}
345
346impl ser::SerializeTuple for SeqSerializer<'_> {
347    type Ok = ();
348    type Error = Error;
349
350    fn serialize_element<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<()> {
351        ser::SerializeSeq::serialize_element(self, value)
352    }
353
354    fn end(self) -> Result<()> {
355        ser::SerializeSeq::end(self)
356    }
357}
358
359impl ser::SerializeTupleStruct for SeqSerializer<'_> {
360    type Ok = ();
361    type Error = Error;
362
363    fn serialize_field<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<()> {
364        ser::SerializeSeq::serialize_element(self, value)
365    }
366
367    fn end(self) -> Result<()> {
368        ser::SerializeSeq::end(self)
369    }
370}
371
372impl ser::SerializeTupleVariant for SeqSerializer<'_> {
373    type Ok = ();
374    type Error = Error;
375
376    fn serialize_field<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<()> {
377        ser::SerializeSeq::serialize_element(self, value)
378    }
379
380    fn end(self) -> Result<()> {
381        // Emit as `variant = { item1\n  item2\n … }` so the parser produces
382        // Object({"variant": Array([…])}), matching what deserialize_enum expects.
383        let variant = self.variant.ok_or_else(|| {
384            Error::Message("variant name missing in SerializeTupleVariant::end".into())
385        })?;
386        let pad = self.parent.pad();
387        let inner_pad = " ".repeat(self.parent.indent + 2);
388        writeln!(self.parent.output, "{pad}{} = {{", escape(&variant))
389            .map_err(|e| Error::Message(e.to_string()))?;
390        for item in &self.items {
391            writeln!(self.parent.output, "{inner_pad}{item}")
392                .map_err(|e| Error::Message(e.to_string()))?;
393        }
394        writeln!(self.parent.output, "{pad}}}").map_err(|e| Error::Message(e.to_string()))
395    }
396}
397
398// ---------------------------------------------------------------------------
399// KeySerializer – accepts only string keys; rejects everything else
400// ---------------------------------------------------------------------------
401
402/// A minimal serializer that collects a `str`/`String` map key and returns
403/// [`Error::KeyMustBeString`] for every other type, including `char` and
404/// unit-enum variants.  Only `serialize_str` (and `serialize_newtype_struct`
405/// delegating to it) succeed.
406struct KeySerializer(String);
407
408impl ser::Serializer for &mut KeySerializer {
409    type Ok = ();
410    type Error = Error;
411    type SerializeSeq = ser::Impossible<(), Error>;
412    type SerializeTuple = ser::Impossible<(), Error>;
413    type SerializeTupleStruct = ser::Impossible<(), Error>;
414    type SerializeTupleVariant = ser::Impossible<(), Error>;
415    type SerializeMap = ser::Impossible<(), Error>;
416    type SerializeStruct = ser::Impossible<(), Error>;
417    type SerializeStructVariant = ser::Impossible<(), Error>;
418
419    fn serialize_str(self, v: &str) -> Result<()> {
420        v.clone_into(&mut self.0);
421        Ok(())
422    }
423
424    fn serialize_bool(self, _v: bool) -> Result<()> {
425        Err(Error::KeyMustBeString)
426    }
427    fn serialize_i8(self, _v: i8) -> Result<()> {
428        Err(Error::KeyMustBeString)
429    }
430    fn serialize_i16(self, _v: i16) -> Result<()> {
431        Err(Error::KeyMustBeString)
432    }
433    fn serialize_i32(self, _v: i32) -> Result<()> {
434        Err(Error::KeyMustBeString)
435    }
436    fn serialize_i64(self, _v: i64) -> Result<()> {
437        Err(Error::KeyMustBeString)
438    }
439    fn serialize_u8(self, _v: u8) -> Result<()> {
440        Err(Error::KeyMustBeString)
441    }
442    fn serialize_u16(self, _v: u16) -> Result<()> {
443        Err(Error::KeyMustBeString)
444    }
445    fn serialize_u32(self, _v: u32) -> Result<()> {
446        Err(Error::KeyMustBeString)
447    }
448    fn serialize_u64(self, _v: u64) -> Result<()> {
449        Err(Error::KeyMustBeString)
450    }
451    fn serialize_f32(self, _v: f32) -> Result<()> {
452        Err(Error::KeyMustBeString)
453    }
454    fn serialize_f64(self, _v: f64) -> Result<()> {
455        Err(Error::KeyMustBeString)
456    }
457    fn serialize_char(self, _v: char) -> Result<()> {
458        Err(Error::KeyMustBeString)
459    }
460    fn serialize_bytes(self, _v: &[u8]) -> Result<()> {
461        Err(Error::KeyMustBeString)
462    }
463    fn serialize_none(self) -> Result<()> {
464        Err(Error::KeyMustBeString)
465    }
466    fn serialize_some<T: Serialize + ?Sized>(self, _v: &T) -> Result<()> {
467        Err(Error::KeyMustBeString)
468    }
469    fn serialize_unit(self) -> Result<()> {
470        Err(Error::KeyMustBeString)
471    }
472    fn serialize_unit_struct(self, _name: &'static str) -> Result<()> {
473        Err(Error::KeyMustBeString)
474    }
475    fn serialize_unit_variant(
476        self,
477        _name: &'static str,
478        _idx: u32,
479        _variant: &'static str,
480    ) -> Result<()> {
481        Err(Error::KeyMustBeString)
482    }
483    fn serialize_newtype_struct<T: Serialize + ?Sized>(
484        self,
485        _name: &'static str,
486        value: &T,
487    ) -> Result<()> {
488        value.serialize(self)
489    }
490    fn serialize_newtype_variant<T: Serialize + ?Sized>(
491        self,
492        _name: &'static str,
493        _idx: u32,
494        _variant: &'static str,
495        _value: &T,
496    ) -> Result<()> {
497        Err(Error::KeyMustBeString)
498    }
499    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
500        Err(Error::KeyMustBeString)
501    }
502    fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple> {
503        Err(Error::KeyMustBeString)
504    }
505    fn serialize_tuple_struct(
506        self,
507        _name: &'static str,
508        _len: usize,
509    ) -> Result<Self::SerializeTupleStruct> {
510        Err(Error::KeyMustBeString)
511    }
512    fn serialize_tuple_variant(
513        self,
514        _name: &'static str,
515        _idx: u32,
516        _variant: &'static str,
517        _len: usize,
518    ) -> Result<Self::SerializeTupleVariant> {
519        Err(Error::KeyMustBeString)
520    }
521    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
522        Err(Error::KeyMustBeString)
523    }
524    fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
525        Err(Error::KeyMustBeString)
526    }
527    fn serialize_struct_variant(
528        self,
529        _name: &'static str,
530        _idx: u32,
531        _variant: &'static str,
532        _len: usize,
533    ) -> Result<Self::SerializeStructVariant> {
534        Err(Error::KeyMustBeString)
535    }
536}
537
538// ---------------------------------------------------------------------------
539// MapSerializer – handles maps and structs
540// ---------------------------------------------------------------------------
541
542/// [`ser::SerializeMap`] (and struct impls) that writes `key = value` /
543/// `key { … }` entries one at a time.
544pub struct MapSerializer<'a> {
545    ser: &'a mut Serializer,
546    /// The most recently serialized key, waiting for its value.
547    current_key: Option<String>,
548    /// If `Some`, wrap all emitted content in a `variant_name { … }` block.
549    variant_name: Option<String>,
550}
551
552impl MapSerializer<'_> {
553    /// Serialize a single `key = value` or `key { … }` entry into `self.ser`.
554    fn write_kv<T: Serialize + ?Sized>(&mut self, key: &str, value: &T) -> Result<()> {
555        let indent = self.ser.indent;
556        let pad = " ".repeat(indent);
557
558        // Serialize the value at the *current* indentation level.  This single
559        // call is sufficient for scalars and for array blocks, whose output is
560        // already formatted correctly:
561        //
562        //   scalar  →  no newlines, used directly as the RHS of `key = value`
563        //   array   →  `{\n<items at indent+2>\n<indent>}\n`, written inline
564        //               as `key = {…}` by `writeln!` below
565        //
566        // Only struct/map (multi-line, not starting with `{` or `"`) needs the
567        // content indented two levels deeper than the current key, so we
568        // re-serialize those at `indent+2`.  This is the only case where
569        // `Serialize` is invoked twice.
570        let mut first = Serializer::new(indent);
571        value.serialize(&mut first)?;
572        let rendered = first.output;
573
574        if rendered.contains('\n')
575            && !rendered.trim_start().starts_with('{')
576            && !rendered.trim_start().starts_with('"')
577        {
578            // Multi-line object block → `key {\n  <fields at indent+2>\n}\n`
579            // Re-serialize at the correct child indentation so nested fields
580            // sit two levels deeper than the enclosing key.  We must not
581            // blindly re-indent the first-pass output line-by-line because
582            // doing so would corrupt any quoted scalar whose value contains a
583            // literal newline (the continuation line is not a separate field).
584            writeln!(self.ser.output, "{pad}{} {{", escape(key))
585                .map_err(|e| Error::Message(e.to_string()))?;
586            let mut inner = Serializer::new(indent + 2);
587            value.serialize(&mut inner)?;
588            self.ser.output.push_str(&inner.output);
589            writeln!(self.ser.output, "{pad}}}").map_err(|e| Error::Message(e.to_string()))?;
590        } else if rendered.contains('\n') {
591            // Multi-line array block starting with `{`.
592            // The first-pass output is already at the right indentation
593            // (`{` inline, items at `indent+2`, `}` at `indent`), so we
594            // reuse it without a second `serialize` call.
595            writeln!(
596                self.ser.output,
597                "{pad}{} = {}",
598                escape(key),
599                rendered.trim_end()
600            )
601            .map_err(|e| Error::Message(e.to_string()))?;
602        } else {
603            // Scalar (no newlines, or a quoted scalar — both fit on one line).
604            let rendered = rendered.trim_end_matches('\n');
605            writeln!(self.ser.output, "{pad}{} = {rendered}", escape(key))
606                .map_err(|e| Error::Message(e.to_string()))?;
607        }
608        Ok(())
609    }
610}
611
612impl ser::SerializeMap for MapSerializer<'_> {
613    type Ok = ();
614    type Error = Error;
615
616    fn serialize_key<T: Serialize + ?Sized>(&mut self, key: &T) -> Result<()> {
617        let mut ks = KeySerializer(String::new());
618        key.serialize(&mut ks)?;
619        self.current_key = Some(ks.0);
620        Ok(())
621    }
622
623    fn serialize_value<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<()> {
624        let key = self
625            .current_key
626            .take()
627            .ok_or_else(|| Error::Parse("serialize_value called before serialize_key".into()))?;
628        self.write_kv(&key, value)
629    }
630
631    fn end(self) -> Result<()> {
632        if let Some(variant) = self.variant_name {
633            // Wrap the content we already wrote in `variant { … }`.
634            let pad = " ".repeat(self.ser.indent.saturating_sub(2));
635            let header = format!("{pad}{} {{\n", escape(&variant));
636            let footer = format!("{pad}}}\n");
637            self.ser.output.insert_str(0, &header);
638            self.ser.output.push_str(&footer);
639        }
640        Ok(())
641    }
642}
643
644impl ser::SerializeStruct for MapSerializer<'_> {
645    type Ok = ();
646    type Error = Error;
647
648    fn serialize_field<T: Serialize + ?Sized>(
649        &mut self,
650        key: &'static str,
651        value: &T,
652    ) -> Result<()> {
653        self.write_kv(key, value)
654    }
655
656    fn end(self) -> Result<()> {
657        ser::SerializeMap::end(self)
658    }
659}
660
661impl ser::SerializeStructVariant for MapSerializer<'_> {
662    type Ok = ();
663    type Error = Error;
664
665    fn serialize_field<T: Serialize + ?Sized>(
666        &mut self,
667        key: &'static str,
668        value: &T,
669    ) -> Result<()> {
670        self.write_kv(key, value)
671    }
672
673    fn end(self) -> Result<()> {
674        ser::SerializeMap::end(self)
675    }
676}