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#[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 pub const fn new() -> SerializerConfig {
79 SerializerConfig {
80 separator: "__",
81 case: Case::Upper,
82 context: deser_core::Context::new(),
83 }
84 }
85
86 pub const fn builder() -> SerializerConfigBuilder {
88 SerializerConfigBuilder::new()
89 }
90
91 pub const fn into_builder(self) -> SerializerConfigBuilder {
93 SerializerConfigBuilder { value: self }
94 }
95
96 pub fn set_context(&mut self, context: deser_core::Context) {
104 self.context = context;
105 }
106
107 pub fn context(&self) -> &deser_core::Context {
109 &self.context
110 }
111
112 #[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 pub const fn set_separator(&mut self, separator: &'static str) {
126 self.separator = separator;
127 }
128
129 pub const fn set_case(&mut self, case: Case) {
133 self.case = case;
134 }
135
136 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 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#[derive(Debug, Clone)]
181#[must_use]
182pub struct SerializerConfigBuilder {
183 value: SerializerConfig,
184}
185
186impl SerializerConfigBuilder {
187 pub const fn new() -> SerializerConfigBuilder {
189 SerializerConfigBuilder {
190 value: SerializerConfig::new(),
191 }
192 }
193
194 pub const fn separator(mut self, separator: &'static str) -> SerializerConfigBuilder {
198 self.value.set_separator(separator);
199 self
200 }
201
202 pub const fn case(mut self, case: Case) -> SerializerConfigBuilder {
206 self.value.set_case(case);
207 self
208 }
209
210 pub fn context(mut self, context: deser_core::Context) -> SerializerConfigBuilder {
214 self.value.set_context(context);
215 self
216 }
217
218 pub const fn build(self) -> SerializerConfig {
220 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
235pub 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
259enum Frame {
261 Map { prefix: usize },
263 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
276struct Writer<'c> {
278 config: &'c SerializerConfig,
279 bytes: BytesFormat,
281 out: Vec<(String, String)>,
282 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 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 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 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 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 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 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
470fn 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
478fn 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 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 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}