Skip to main content

deser_env/
ser.rs

1use std::borrow::Cow;
2use std::collections::HashMap;
3
4use deser_core::ext::Number;
5use deser_core::ser::SerializeDriver;
6use deser_core::{Atom, BytesFormat, Error, ErrorKind, Event, Serialize, State};
7
8use crate::Case;
9
10/// Configures how values are serialized into environment variables.
11///
12/// The value has to serialize to a map (for instance a struct or a map
13/// type).  The names are the keys with the prefix in front, nested keys are
14/// joined with the separator and uppercased (see [`Case`]).  Keys whose
15/// names would not read back as the same keys are an error: keys that
16/// contain the separator (or end with a part of it), and different keys
17/// with the same name (like `a` and `A`).  Sequences are
18/// written with indexes (`APP_HOSTS__0`), use the
19/// [`Separated`](deser_core::adapters::Separated) adapter to write them into
20/// a single variable.  Null values (like `None`) of map entries are skipped,
21/// null values in sequences are written as empty values.  Maps and sequences
22/// that are empty are not written as variables cannot represent them.
23///
24/// Numbers are written with the shortest text that reads back as the same
25/// value, booleans as `true` and `false` and bytes as base64 (or the
26/// [`BytesFormat`](deser_core::BytesFormat) of the context).  Keys that are empty or contain the separator
27/// are an error as they would not read back.
28///
29/// ```
30/// use deser_env::SerializerConfig;
31///
32/// #[derive(deser::Serialize)]
33/// struct Config {
34///     name: &'static str,
35///     server: Server,
36///     hosts: Vec<&'static str>,
37/// }
38///
39/// #[derive(deser::Serialize)]
40/// struct Server {
41///     port: u16,
42///     timeout: Option<u32>,
43/// }
44///
45/// let config = Config {
46///     name: "shop",
47///     server: Server { port: 80, timeout: None },
48///     hosts: vec!["a", "b"],
49/// };
50/// let vars = SerializerConfig::new().to_vars("APP_", &config).unwrap();
51/// let vars: Vec<_> =
52///     vars.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect();
53/// assert_eq!(
54///     vars,
55///     [
56///         ("APP_NAME", "shop"),
57///         ("APP_SERVER__PORT", "80"),
58///         ("APP_HOSTS__0", "a"),
59///         ("APP_HOSTS__1", "b"),
60///     ]
61/// );
62/// ```
63#[derive(Debug, Clone, PartialEq, Eq)]
64pub struct SerializerConfig {
65    separator: &'static str,
66    case: Case,
67    context: deser_core::Context,
68}
69
70impl Default for SerializerConfig {
71    fn default() -> SerializerConfig {
72        SerializerConfig::new()
73    }
74}
75
76impl SerializerConfig {
77    /// Creates the default configuration.
78    pub const fn new() -> SerializerConfig {
79        SerializerConfig {
80            separator: "__",
81            case: Case::Upper,
82            context: deser_core::Context::new(),
83        }
84    }
85
86    /// Returns a builder for the configuration (see [`SerializerConfigBuilder`]).
87    pub const fn builder() -> SerializerConfigBuilder {
88        SerializerConfigBuilder::new()
89    }
90
91    /// Returns a builder that starts with this configuration.
92    pub const fn into_builder(self) -> SerializerConfigBuilder {
93        SerializerConfigBuilder { value: self }
94    }
95
96    /// Sets the context the values are serialized in.
97    ///
98    /// The values of the context are the defaults of the extension values
99    /// of the state (see [`Context`](deser_core::Context)), for instance
100    /// the [`BytesFormat`](deser_core::BytesFormat).  A context set on the
101    /// driver (for instance in the setup callback of
102    /// [`to_vars_with`](Self::to_vars_with)) takes precedence.
103    pub fn set_context(&mut self, context: deser_core::Context) {
104        self.context = context;
105    }
106
107    /// Returns the context the values are serialized in.
108    pub fn context(&self) -> &deser_core::Context {
109        &self.context
110    }
111
112    /// Gives the context to a driver which has none.
113    #[inline]
114    fn apply_context(&self, driver: &mut SerializeDriver<'_>) {
115        if !self.context.is_empty() {
116            driver.set_default_context(self.context.clone());
117        }
118    }
119
120    /// Sets the separator of nested keys.
121    ///
122    /// The default is `__`, see
123    /// [`DeserializerConfig::set_separator`](crate::DeserializerConfig::set_separator).
124    /// With the empty string nested maps and sequences are an error.
125    pub const fn set_separator(&mut self, separator: &'static str) {
126        self.separator = separator;
127    }
128
129    /// Sets how keys map onto names.
130    ///
131    /// The default is [`Case::Upper`] which uppercases keys.
132    pub const fn set_case(&mut self, case: Case) {
133        self.case = case;
134    }
135
136    /// Serializes a value into variables with a prefix.
137    ///
138    /// See [`to_vars`].
139    pub fn to_vars<T: Serialize + ?Sized>(
140        &self,
141        prefix: &str,
142        value: &T,
143    ) -> Result<Vec<(String, String)>, Error> {
144        self.to_vars_with(prefix, value, |_| {})
145    }
146
147    /// Serializes a value into variables with a prefix and a configured
148    /// driver.
149    ///
150    /// The callback is invoked with the driver before the serialization
151    /// starts, for instance to add [`Layer`](deser_core::ser::Layer)s.
152    pub fn to_vars_with<F, T: Serialize + ?Sized>(
153        &self,
154        prefix: &str,
155        value: &T,
156        setup: F,
157    ) -> Result<Vec<(String, String)>, Error>
158    where
159        F: FnOnce(&mut SerializeDriver<'_>),
160    {
161        let mut driver = SerializeDriver::new(&value);
162        setup(&mut driver);
163        self.apply_context(&mut driver);
164        let mut writer = Writer {
165            config: self,
166            bytes: BytesFormat::of(driver.state()),
167            out: Vec::new(),
168            name: prefix.to_string(),
169            stack: Vec::new(),
170        };
171        driver.drive(|event, state| writer.event(event, state))?;
172        writer.check_names(prefix.len())?;
173        Ok(writer.out)
174    }
175}
176
177/// Builds a [`SerializerConfig`].
178///
179/// The methods have the names of the setters of [`SerializerConfig`] (without `set_`).
180#[derive(Debug, Clone)]
181#[must_use]
182pub struct SerializerConfigBuilder {
183    value: SerializerConfig,
184}
185
186impl SerializerConfigBuilder {
187    /// Creates a builder that starts with the default.
188    pub const fn new() -> SerializerConfigBuilder {
189        SerializerConfigBuilder {
190            value: SerializerConfig::new(),
191        }
192    }
193
194    /// Sets the separator of nested keys.
195    ///
196    /// See [`SerializerConfig::set_separator`].
197    pub const fn separator(mut self, separator: &'static str) -> SerializerConfigBuilder {
198        self.value.set_separator(separator);
199        self
200    }
201
202    /// Sets how keys map onto names.
203    ///
204    /// See [`SerializerConfig::set_case`].
205    pub const fn case(mut self, case: Case) -> SerializerConfigBuilder {
206        self.value.set_case(case);
207        self
208    }
209
210    /// Sets the context the values are serialized in.
211    ///
212    /// See [`SerializerConfig::set_context`].
213    pub fn context(mut self, context: deser_core::Context) -> SerializerConfigBuilder {
214        self.value.set_context(context);
215        self
216    }
217
218    /// Returns the built [`SerializerConfig`].
219    pub const fn build(self) -> SerializerConfig {
220        // the value cannot be moved out of the builder in a const fn as the
221        // builder needs dropping (the context has a destructor)
222        // SAFETY: the value is read once and the builder is forgotten
223        let value = unsafe { core::ptr::read(&self.value) };
224        core::mem::forget(self);
225        value
226    }
227}
228
229impl Default for SerializerConfigBuilder {
230    fn default() -> SerializerConfigBuilder {
231        SerializerConfigBuilder::new()
232    }
233}
234
235/// Serializes a value into environment variables with a prefix.
236///
237/// This uses the default [`SerializerConfig`], see there for more
238/// information.  The variables are name-value pairs which can for instance
239/// be passed to a child process:
240///
241/// ```no_run
242/// use std::process::Command;
243///
244/// #[derive(deser::Serialize)]
245/// struct Config {
246///     port: u16,
247/// }
248///
249/// let vars = deser_env::to_vars("APP_", &Config { port: 80 }).unwrap();
250/// Command::new("server").envs(vars).spawn().unwrap();
251/// ```
252pub fn to_vars<T: Serialize + ?Sized>(
253    prefix: &str,
254    value: &T,
255) -> Result<Vec<(String, String)>, Error> {
256    SerializerConfig::new().to_vars(prefix, value)
257}
258
259/// A container that is being written.
260enum Frame {
261    /// A map, with the length of its name.
262    Map { prefix: usize },
263    /// A sequence, with the length of its name and the index of the next
264    /// element.
265    Seq { prefix: usize, index: usize },
266}
267
268impl Frame {
269    fn prefix(&self) -> usize {
270        match *self {
271            Frame::Map { prefix } | Frame::Seq { prefix, .. } => prefix,
272        }
273    }
274}
275
276/// Writes the events of a value.
277struct Writer<'c> {
278    config: &'c SerializerConfig,
279    /// How bytes are written (from the state).
280    bytes: BytesFormat,
281    out: Vec<(String, String)>,
282    /// The name of the current value.
283    name: String,
284    stack: Vec<Frame>,
285}
286
287impl Writer<'_> {
288    fn event(&mut self, event: Event, state: &State) -> Result<(), Error> {
289        match (self.stack.last_mut(), event) {
290            (None, Event::MapStart(_)) => self.stack.push(Frame::Map {
291                prefix: self.name.len(),
292            }),
293            (None, Event::Atom(Atom::Null)) => {}
294            (None, _) => {
295                return Err(Error::new(
296                    ErrorKind::UnsupportedType,
297                    "environment variables hold maps (like structs)",
298                ));
299            }
300
301            (Some(Frame::Map { .. }), Event::MapEnd) => {
302                self.stack.pop();
303            }
304            (Some(&mut Frame::Map { prefix }), event) => {
305                if state.is_map_key() {
306                    let key = match event {
307                        Event::Atom(ref atom) => key_text(atom)?,
308                        _ => return Err(unsupported_key()),
309                    };
310                    if key.is_empty() {
311                        return Err(Error::new(
312                            ErrorKind::UnsupportedType,
313                            "keys of environment variables must not be empty",
314                        ));
315                    }
316                    if !self.config.separator.is_empty() && key.contains(self.config.separator) {
317                        return Err(Error::new(
318                            ErrorKind::UnsupportedType,
319                            format!("key {:?} contains the separator", key),
320                        ));
321                    }
322                    self.name.truncate(prefix);
323                    // the keys of the top level map follow the prefix
324                    if self.stack.len() > 1 {
325                        self.name.push_str(self.config.separator);
326                    }
327                    self.push_key(&key);
328                } else {
329                    self.value(event, false)?;
330                }
331            }
332
333            (Some(Frame::Seq { .. }), Event::SeqEnd) => {
334                self.stack.pop();
335            }
336            (
337                Some(&mut Frame::Seq {
338                    prefix,
339                    ref mut index,
340                }),
341                event,
342            ) => {
343                let element = *index;
344                *index += 1;
345                self.name.truncate(prefix);
346                self.name.push_str(self.config.separator);
347                self.name.push_str(&element.to_string());
348                self.value(event, true)?;
349            }
350        }
351        Ok(())
352    }
353
354    /// Appends a key to the name.
355    fn push_key(&mut self, key: &str) {
356        match self.config.case {
357            Case::Upper => self
358                .name
359                .extend(key.chars().map(|c| c.to_ascii_uppercase())),
360            Case::Preserve => self.name.push_str(key),
361        }
362    }
363
364    /// Checks that no name is written twice and that no name is nested
365    /// in another one.
366    ///
367    /// Different keys can have the same name, for instance `a` and `A`
368    /// (names are uppercased) or `1` and `"1"`.
369    fn check_names(&self, prefix: usize) -> Result<(), Error> {
370        let normalize = |name: &str| match self.config.case {
371            Case::Upper => name.to_ascii_lowercase(),
372            Case::Preserve => name.to_string(),
373        };
374        let names: HashMap<String, &str> = self
375            .out
376            .iter()
377            .map(|(name, _)| (normalize(name), name.as_str()))
378            .collect();
379        if names.len() != self.out.len() {
380            return Err(Error::new(
381                ErrorKind::UnsupportedType,
382                "different keys have the same name",
383            ));
384        }
385        let mut segments = Vec::new();
386        for name in names.keys() {
387            segments.clear();
388            crate::de::split_name(&name[prefix..], self.config.separator, &mut segments);
389            for &(_, end) in &segments[..segments.len() - 1] {
390                if let Some(parent) = names.get(&name[..prefix + end]) {
391                    return Err(Error::new(
392                        ErrorKind::UnsupportedType,
393                        format!("the variable {parent:?} has a value and nested variables"),
394                    ));
395                }
396            }
397        }
398        Ok(())
399    }
400
401    /// Checks that the current name splits into its keys again.
402    ///
403    /// Keys do not contain the separator, but the end of a key and the
404    /// separator after it can (`A_` and `__` are `A___`, which splits into
405    /// `A` and `_`).
406    fn check_name(&self) -> Result<(), Error> {
407        let Some(base) = self.stack.first().map(Frame::prefix) else {
408            return Ok(());
409        };
410        let separator = self.config.separator;
411        let mut segments = Vec::new();
412        crate::de::split_name(&self.name[base..], separator, &mut segments);
413        let expected = self.stack.iter().enumerate().map(|(index, frame)| {
414            let start = match index {
415                0 => frame.prefix(),
416                _ => frame.prefix() + separator.len(),
417            };
418            let end = match self.stack.get(index + 1) {
419                Some(next) => next.prefix(),
420                None => self.name.len(),
421            };
422            (start - base, end - base)
423        });
424        if segments.iter().copied().eq(expected) {
425            Ok(())
426        } else {
427            Err(Error::new(
428                ErrorKind::UnsupportedType,
429                format!(
430                    "the name {:?} does not split into its keys at the separator",
431                    self.name
432                ),
433            ))
434        }
435    }
436
437    /// Writes a value for the current name.
438    fn value(&mut self, event: Event, in_seq: bool) -> Result<(), Error> {
439        match event {
440            Event::Atom(atom) => {
441                let value = match value_text(&atom, self.bytes)? {
442                    Some(value) => value,
443                    // nulls in sequences are empty values to keep the
444                    // positions of the other values
445                    None if in_seq => Cow::Borrowed(""),
446                    None => return Ok(()),
447                };
448                self.check_name()?;
449                self.out.push((self.name.clone(), value.into_owned()));
450            }
451            Event::MapStart(_) | Event::SeqStart(_) if self.config.separator.is_empty() => {
452                return Err(Error::new(
453                    ErrorKind::UnsupportedType,
454                    "nested maps and sequences require a separator",
455                ));
456            }
457            Event::MapStart(_) => self.stack.push(Frame::Map {
458                prefix: self.name.len(),
459            }),
460            Event::SeqStart(_) => self.stack.push(Frame::Seq {
461                prefix: self.name.len(),
462                index: 0,
463            }),
464            Event::MapEnd | Event::SeqEnd => unreachable!("ends are handled by the frames"),
465        }
466        Ok(())
467    }
468}
469
470/// Returns the text of a map key.
471fn key_text<'a>(atom: &'a Atom<'_>) -> Result<Cow<'a, str>, Error> {
472    match atom {
473        Atom::Null | Atom::Bytes(_) => Err(unsupported_key()),
474        atom => value_text(atom, BytesFormat::BASE64)?.ok_or_else(unsupported_key),
475    }
476}
477
478/// Returns the text of a value, `None` for null.
479fn value_text<'a>(atom: &'a Atom<'_>, bytes: BytesFormat) -> Result<Option<Cow<'a, str>>, Error> {
480    Ok(Some(match *atom {
481        Atom::Null => return Ok(None),
482        // values whose type was inferred from text are written as value
483        Atom::Implicit(ref value) => {
484            return Ok(value_text(&value.value().to_atom(), bytes)?
485                .map(|text| Cow::Owned(text.into_owned())));
486        }
487        Atom::Bool(value) => Cow::Borrowed(if value { "true" } else { "false" }),
488        Atom::Str(ref value) | Atom::Lexical(ref value) => Cow::Borrowed(&**value),
489        Atom::Char(value) => Cow::Owned(value.to_string()),
490        Atom::U64(value) => Cow::Owned(value.to_string()),
491        Atom::I64(value) => Cow::Owned(value.to_string()),
492        Atom::F32(value) => Cow::Owned(zmij::Buffer::new().format(value).into()),
493        Atom::F64(value) => Cow::Owned(zmij::Buffer::new().format(value).into()),
494        Atom::Bytes(ref value) => {
495            let format = value.fallback.copied().unwrap_or(bytes);
496            Cow::Owned(
497                format
498                    .encode(value)
499                    .or_else(|| BytesFormat::BASE64.encode(value))
500                    .unwrap_or_default(),
501            )
502        }
503        Atom::Ext(ref ext) => {
504            if let Some(number) = ext.downcast_value_ref::<Number>() {
505                // numbers keep their text
506                Cow::Owned(number.as_str().to_string())
507            } else if let Some(value) = ext.downcast_ref::<u128>() {
508                Cow::Owned(value.to_string())
509            } else if let Some(value) = ext.downcast_ref::<i128>() {
510                Cow::Owned(value.to_string())
511            } else {
512                match ext.fallback() {
513                    Atom::Ext(_) => {
514                        return Err(Error::new(
515                            ErrorKind::UnsupportedType,
516                            format!("environment variables do not support {}", ext.name()),
517                        ));
518                    }
519                    fallback => match value_text(&fallback, bytes)? {
520                        Some(text) => Cow::Owned(text.into_owned()),
521                        None => return Ok(None),
522                    },
523                }
524            }
525        }
526        _ => {
527            return Err(Error::new(
528                ErrorKind::UnsupportedType,
529                format!("environment variables do not support {}", atom.name()),
530            ));
531        }
532    }))
533}
534
535#[cold]
536fn unsupported_key() -> Error {
537    Error::new(
538        ErrorKind::UnsupportedType,
539        "keys of environment variables must be strings, numbers or booleans",
540    )
541}