smart_config/de/
repeated.rs

1//! `Repeated` deserializer for arrays / objects.
2
3use std::{
4    borrow::Cow,
5    collections::{BTreeMap, BTreeSet, HashMap, HashSet},
6    fmt,
7    hash::{BuildHasher, Hash},
8    marker::PhantomData,
9    sync::Arc,
10};
11
12use serde::de::Error as DeError;
13
14use crate::{
15    de::{DeserializeContext, DeserializeParam, WellKnown, WellKnownOption},
16    error::{ErrorWithOrigin, LowLevelError},
17    metadata::{BasicTypes, ParamMetadata, TypeDescription},
18    pat::Split,
19    utils::const_eq,
20    value::{StrValue, Value, ValueOrigin, WithOrigin},
21};
22
23/// Deserializer from JSON arrays.
24///
25/// Supports deserializing to [`Vec`], arrays, [`HashSet`], [`BTreeSet`].
26#[derive(Debug)]
27pub struct Repeated<De>(pub De);
28
29impl<De> Repeated<De> {
30    fn deserialize_array<T, C>(
31        &self,
32        mut ctx: DeserializeContext<'_>,
33        param: &'static ParamMetadata,
34        expected_len: Option<usize>,
35    ) -> Result<C, ErrorWithOrigin>
36    where
37        De: DeserializeParam<T>,
38        C: FromIterator<T>,
39    {
40        let deserializer = ctx.current_value_deserializer(param.name)?;
41        let Value::Array(items) = deserializer.value() else {
42            return Err(deserializer.invalid_type("array"));
43        };
44
45        if let Some(expected_len) = expected_len
46            && items.len() != expected_len
47        {
48            let err = DeError::invalid_length(items.len(), &expected_len.to_string().as_str());
49            return Err(deserializer.enrich_err(err));
50        }
51
52        let mut has_errors = false;
53        let items = items.iter().enumerate().filter_map(|(i, item)| {
54            let coerced = item.coerce_value_type(De::EXPECTING);
55            let mut child_ctx = ctx.child(&i.to_string(), ctx.location_in_config);
56            let mut child_ctx = child_ctx.patched(coerced.as_ref().unwrap_or(item));
57            match self.0.deserialize_param(child_ctx.borrow(), param) {
58                Ok(val) if !has_errors => Some(val),
59                Ok(_) => None, // Drop the value since it won't be needed anyway
60                Err(err) => {
61                    has_errors = true;
62                    child_ctx.push_error(err);
63                    None
64                }
65            }
66        });
67        let items: C = items.collect();
68
69        if has_errors {
70            let origin = deserializer.origin().clone();
71            Err(ErrorWithOrigin::new(LowLevelError::InvalidArray, origin))
72        } else {
73            Ok(items)
74        }
75    }
76}
77
78macro_rules! impl_serialization_for_repeated {
79    ($param:ty) => {
80        fn deserialize_param(
81            &self,
82            ctx: DeserializeContext<'_>,
83            param: &'static ParamMetadata,
84        ) -> Result<$param, ErrorWithOrigin> {
85            self.deserialize_array(ctx, param, None)
86        }
87
88        fn serialize_param(&self, param: &$param) -> serde_json::Value {
89            let array = param
90                .iter()
91                .map(|item| self.0.serialize_param(item))
92                .collect();
93            serde_json::Value::Array(array)
94        }
95    };
96}
97
98impl<T: 'static, De> DeserializeParam<Vec<T>> for Repeated<De>
99where
100    De: DeserializeParam<T>,
101{
102    const EXPECTING: BasicTypes = BasicTypes::ARRAY;
103
104    fn describe(&self, description: &mut TypeDescription) {
105        description.set_items(&self.0);
106    }
107
108    impl_serialization_for_repeated!(Vec<T>);
109}
110
111impl<T, S, De> DeserializeParam<HashSet<T, S>> for Repeated<De>
112where
113    T: 'static + Eq + Hash,
114    S: 'static + Default + BuildHasher,
115    De: DeserializeParam<T>,
116{
117    const EXPECTING: BasicTypes = BasicTypes::ARRAY;
118
119    fn describe(&self, description: &mut TypeDescription) {
120        description.set_details("set").set_items(&self.0);
121    }
122
123    impl_serialization_for_repeated!(HashSet<T, S>);
124}
125
126impl<T, De> DeserializeParam<BTreeSet<T>> for Repeated<De>
127where
128    T: 'static + Eq + Ord,
129    De: DeserializeParam<T>,
130{
131    const EXPECTING: BasicTypes = BasicTypes::ARRAY;
132
133    fn describe(&self, description: &mut TypeDescription) {
134        description.set_details("set").set_items(&self.0);
135    }
136
137    impl_serialization_for_repeated!(BTreeSet<T>);
138}
139
140impl<T: 'static, De, const N: usize> DeserializeParam<[T; N]> for Repeated<De>
141where
142    De: DeserializeParam<T>,
143{
144    const EXPECTING: BasicTypes = BasicTypes::ARRAY;
145
146    fn describe(&self, description: &mut TypeDescription) {
147        description
148            .set_details(format!("{N}-element array"))
149            .set_items(&self.0);
150    }
151
152    fn deserialize_param(
153        &self,
154        ctx: DeserializeContext<'_>,
155        param: &'static ParamMetadata,
156    ) -> Result<[T; N], ErrorWithOrigin> {
157        let items: Vec<_> = self.deserialize_array(ctx, param, Some(N))?;
158        // `unwrap()` is safe due to the length check in `deserialize_inner()`
159        Ok(items.try_into().ok().unwrap())
160    }
161
162    fn serialize_param(&self, param: &[T; N]) -> serde_json::Value {
163        let array = param
164            .iter()
165            .map(|item| self.0.serialize_param(item))
166            .collect();
167        serde_json::Value::Array(array)
168    }
169}
170
171impl<T: WellKnown> WellKnown for Vec<T> {
172    type Deserializer = Repeated<T::Deserializer>;
173    const DE: Self::Deserializer = Repeated(T::DE);
174}
175
176impl<T: WellKnown> WellKnownOption for Vec<T> {}
177
178impl<T: WellKnown, const N: usize> WellKnown for [T; N] {
179    type Deserializer = Repeated<T::Deserializer>;
180    const DE: Self::Deserializer = Repeated(T::DE);
181}
182
183impl<T: WellKnown, const N: usize> WellKnownOption for [T; N] {}
184
185// Heterogeneous tuples don't look like a good idea to mark as well-known because they wouldn't look well-structured
186// (it'd be better to define either multiple params or a struct param).
187
188impl<T, S> WellKnown for HashSet<T, S>
189where
190    T: Eq + Hash + WellKnown,
191    S: 'static + Default + BuildHasher,
192{
193    type Deserializer = Repeated<T::Deserializer>;
194    const DE: Self::Deserializer = Repeated(T::DE);
195}
196
197impl<T, S> WellKnownOption for HashSet<T, S>
198where
199    T: Eq + Hash + WellKnown,
200    S: 'static + Default + BuildHasher,
201{
202}
203
204impl<T> WellKnown for BTreeSet<T>
205where
206    T: Eq + Ord + WellKnown,
207{
208    type Deserializer = Repeated<T::Deserializer>;
209    const DE: Self::Deserializer = Repeated(T::DE);
210}
211
212impl<T> WellKnownOption for BTreeSet<T> where T: Eq + Ord + WellKnown {}
213
214/// Deserializer from JSON objects.
215///
216/// Supports deserializing to [`HashMap`] and [`BTreeMap`].
217pub struct Entries<K, V, DeK = <K as WellKnown>::Deserializer, DeV = <V as WellKnown>::Deserializer>
218{
219    keys: DeK,
220    values: DeV,
221    _kv: PhantomData<fn(K, V)>,
222}
223
224impl<K, V, DeK: fmt::Debug, DeV: fmt::Debug> fmt::Debug for Entries<K, V, DeK, DeV> {
225    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
226        formatter
227            .debug_struct("Entries")
228            .field("keys", &self.keys)
229            .field("values", &self.values)
230            .finish()
231    }
232}
233
234impl<K: WellKnown, V: WellKnown> Entries<K, V, K::Deserializer, V::Deserializer> {
235    /// `Entries` instance using the [`WellKnown`] deserializers for keys and values.
236    pub const WELL_KNOWN: Self = Self::new(K::DE, V::DE);
237}
238
239impl<K, V, DeK, DeV> Entries<K, V, DeK, DeV>
240where
241    DeK: DeserializeParam<K>,
242    DeV: DeserializeParam<V>,
243{
244    /// Creates a new deserializer instance with provided key and value deserializers.
245    pub const fn new(keys: DeK, values: DeV) -> Self {
246        Self {
247            keys,
248            values,
249            _kv: PhantomData,
250        }
251    }
252
253    /// Converts this to a [`NamedEntries`] instance.
254    ///
255    /// # Panics
256    ///
257    ///  Will panic if either `keys_name` or `values_name` is empty OR they coincide.
258    pub const fn named(
259        self,
260        keys_name: &'static str,
261        values_name: &'static str,
262    ) -> NamedEntries<K, V, DeK, DeV> {
263        assert!(!keys_name.is_empty());
264        assert!(!values_name.is_empty());
265        assert!(
266            !const_eq(keys_name.as_bytes(), values_name.as_bytes()),
267            "Keys and values fields must not coincide"
268        );
269
270        NamedEntries {
271            inner: self,
272            keys_name,
273            values_name,
274        }
275    }
276
277    /// Converts this to a [`DelimitedEntries`] instance.
278    ///
279    /// # Panics
280    ///
281    /// Will panic if `entry_sep` or `key_value_sep` are empty OR if they coincide.
282    pub const fn delimited<ESep: Split, KvSep: Split>(
283        self,
284        entry_sep: ESep,
285        key_value_sep: KvSep,
286    ) -> DelimitedEntries<K, V, DeK, DeV, ESep, KvSep> {
287        DelimitedEntries {
288            entry_sep,
289            key_value_sep,
290            inner: self,
291        }
292    }
293
294    fn deserialize_map<'s, C: FromIterator<(K, V)>>(
295        &self,
296        mut ctx: DeserializeContext<'_>,
297        param: &'static ParamMetadata,
298        map_entries: impl Iterator<Item = (&'s str, Cow<'s, WithOrigin>)>,
299        map_origin: &Arc<ValueOrigin>,
300    ) -> Result<C, ErrorWithOrigin> {
301        let mut has_errors = false;
302        let items = map_entries.filter_map(|(key, value)| {
303            let key_as_value = WithOrigin::new(
304                key.to_owned().into(),
305                Arc::new(ValueOrigin::Synthetic {
306                    source: map_origin.clone(),
307                    transform: "string key".into(),
308                }),
309            );
310            let parsed_key =
311                parse_key_or_value::<K, _>(&mut ctx, param, key, &self.keys, &key_as_value);
312            let parsed_value =
313                parse_key_or_value::<V, _>(&mut ctx, param, key, &self.values, &value);
314
315            has_errors |= parsed_key.is_none() || parsed_value.is_none();
316            Some((parsed_key?, parsed_value?)).filter(|_| !has_errors)
317        });
318        let items: C = items.collect();
319
320        if has_errors {
321            let origin = map_origin.clone();
322            Err(ErrorWithOrigin::new(LowLevelError::InvalidObject, origin))
323        } else {
324            Ok(items)
325        }
326    }
327}
328
329fn parse_key_or_value<T, De: DeserializeParam<T>>(
330    ctx: &mut DeserializeContext<'_>,
331    param: &'static ParamMetadata,
332    key_path: &str,
333    de: &De,
334    val: &WithOrigin,
335) -> Option<T> {
336    let coerced = val.coerce_value_type(De::EXPECTING);
337    let mut child_ctx = ctx.child(key_path, ctx.location_in_config);
338    let mut child_ctx = child_ctx.patched(coerced.as_ref().unwrap_or(val));
339    match de.deserialize_param(child_ctx.borrow(), param) {
340        Ok(val) => Some(val),
341        Err(err) => {
342            child_ctx.push_error(err);
343            None
344        }
345    }
346}
347
348/// Converts a key–value entry into the common format (pair of references to the key and value).
349pub trait ToEntry<'a, K, V>: Copy {
350    /// Performs the conversion.
351    fn to_entry(self) -> (&'a K, &'a V);
352}
353
354impl<'a, K, V> ToEntry<'a, K, V> for &'a (K, V) {
355    fn to_entry(self) -> (&'a K, &'a V) {
356        (&self.0, &self.1)
357    }
358}
359
360impl<'a, K, V> ToEntry<'a, K, V> for (&'a K, &'a V) {
361    fn to_entry(self) -> (&'a K, &'a V) {
362        self
363    }
364}
365
366/// Collection that can iterate over its entries.
367///
368/// Implemented for maps in the standard library, `Vec<(K, V)>`, `Box<[(K, V)]>` etc.
369// Needed as a separate trait with a blank impl since otherwise (if the `&C: IntoIterator<..>` requirement
370// is specified directly on the `DeserializeParam` impls) the compiler explodes, suggesting to specify type params
371// in the proc-macro code.
372pub trait ToEntries<K: 'static, V: 'static> {
373    /// Iterates over entries in the collection.
374    fn to_entries(&self) -> impl Iterator<Item = (&K, &V)>;
375}
376
377// Covers maps
378impl<K: 'static, V: 'static, C> ToEntries<K, V> for C
379where
380    for<'a> &'a C: IntoIterator<Item: ToEntry<'a, K, V>>,
381{
382    fn to_entries(&self) -> impl Iterator<Item = (&K, &V)> {
383        self.into_iter().map(ToEntry::to_entry)
384    }
385}
386
387impl<K, V, C, DeK, DeV> DeserializeParam<C> for Entries<K, V, DeK, DeV>
388where
389    K: 'static,
390    V: 'static,
391    DeK: DeserializeParam<K>,
392    DeV: DeserializeParam<V>,
393    C: FromIterator<(K, V)> + ToEntries<K, V>,
394{
395    const EXPECTING: BasicTypes = {
396        assert!(
397            DeK::EXPECTING.contains(BasicTypes::STRING)
398                || DeK::EXPECTING.contains(BasicTypes::INTEGER),
399            "map keys must be deserializable from strings or ints"
400        );
401        BasicTypes::OBJECT
402    };
403
404    fn describe(&self, description: &mut TypeDescription) {
405        description
406            .set_details("map")
407            .set_entries(&self.keys, &self.values);
408    }
409
410    fn deserialize_param(
411        &self,
412        ctx: DeserializeContext<'_>,
413        param: &'static ParamMetadata,
414    ) -> Result<C, ErrorWithOrigin> {
415        let deserializer = ctx.current_value_deserializer(param.name)?;
416        let Value::Object(map) = deserializer.value() else {
417            return Err(deserializer.invalid_type("object"));
418        };
419        let map_entries = map
420            .iter()
421            .map(|(key, value)| (key.as_str(), Cow::Borrowed(value)));
422        self.deserialize_map(ctx, param, map_entries, deserializer.origin())
423    }
424
425    fn serialize_param(&self, param: &C) -> serde_json::Value {
426        let object = param
427            .to_entries()
428            .map(|(key, value)| {
429                let key = match self.keys.serialize_param(key) {
430                    serde_json::Value::String(s) => s,
431                    serde_json::Value::Number(num) => num.to_string(),
432                    _ => panic!("unsupported key value"),
433                };
434                let value = self.values.serialize_param(value);
435                (key, value)
436            })
437            .collect();
438        serde_json::Value::Object(object)
439    }
440}
441
442impl<K, V, S> WellKnown for HashMap<K, V, S>
443where
444    K: 'static + Eq + Hash + WellKnown,
445    V: 'static + WellKnown,
446    S: 'static + Default + BuildHasher,
447{
448    type Deserializer = Entries<K, V, K::Deserializer, V::Deserializer>;
449    const DE: Self::Deserializer = Entries::new(K::DE, V::DE);
450}
451
452impl<K, V, S> WellKnownOption for HashMap<K, V, S>
453where
454    K: 'static + Eq + Hash + WellKnown,
455    V: 'static + WellKnown,
456    S: 'static + Default + BuildHasher,
457{
458}
459
460impl<K, V> WellKnown for BTreeMap<K, V>
461where
462    K: 'static + Eq + Ord + WellKnown,
463    V: 'static + WellKnown,
464{
465    type Deserializer = Entries<K, V, K::Deserializer, V::Deserializer>;
466    const DE: Self::Deserializer = Entries::new(K::DE, V::DE);
467}
468
469impl<K, V> WellKnownOption for BTreeMap<K, V>
470where
471    K: 'static + Eq + Ord + WellKnown,
472    V: 'static + WellKnown,
473{
474}
475
476/// Deserializer that supports either an array of values, or a string in which values are delimited
477/// by the specified separator.
478///
479/// # Examples
480///
481/// ```
482/// use std::{collections::HashSet, path::PathBuf};
483/// use smart_config::{de, pat::lazy_regex, testing, DescribeConfig, DeserializeConfig};
484///
485/// #[derive(DescribeConfig, DeserializeConfig)]
486/// struct TestConfig {
487///     #[config(default, with = de::Delimited::new(","))]
488///     strings: Vec<String>,
489///     // More complex types are supported as well (along with custom base deserializers).
490///     // Importantly, the base deserializer still refers to the entire collection, not its items,
491///     // so you should use something like `Repeated`, or use the `repeat()` constructor.
492///     #[config(with = de::Delimited::repeat(de::Serde![str], ":"))]
493///     paths: Vec<PathBuf>,
494///     // ...and more complex collections (here together with string -> number coercion
495///     // and a regex-based splitter)
496///     #[config(with = de::Delimited::new(lazy_regex!(ref r"\s*;\s*")))]
497///     ints: HashSet<u64>,
498/// }
499///
500/// let sample = smart_config::config!(
501///     "strings": ["test", "string"], // standard array value is still supported
502///     "paths": "/usr/bin:/usr/local/bin",
503///     "ints": "12; 34 ; 12",
504/// );
505/// let config: TestConfig = testing::test(sample)?;
506/// assert_eq!(config.strings.len(), 2);
507/// assert_eq!(config.strings[0], "test");
508/// assert_eq!(config.paths.len(), 2);
509/// assert_eq!(config.paths[1].as_os_str(), "/usr/local/bin");
510/// assert_eq!(config.ints, HashSet::from([12, 34]));
511/// # anyhow::Ok(())
512/// ```
513///
514/// The wrapping logic is smart enough to catch in compile time an attempt to apply `Delimited` to a type
515/// that cannot be deserialized from an array:
516///
517/// ```compile_fail
518/// use smart_config::{de, DescribeConfig, DeserializeConfig};
519///
520/// #[derive(DescribeConfig, DeserializeConfig)]
521/// struct Fail {
522///     // will fail with "evaluation of `<Delimited as DeserializeParam<u64>>::EXPECTING` failed"
523///     #[config(default, with = de::Delimited::new(","))]
524///     test: u64,
525/// }
526/// ```
527pub struct Delimited<De = (), S = &'static str>(pub De, pub S);
528
529impl<De: fmt::Debug, S: Split> fmt::Debug for Delimited<De, S> {
530    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
531        formatter
532            .debug_tuple("Delimited")
533            .field(&self.0)
534            .field(&self.1.display())
535            .finish()
536    }
537}
538
539impl<S: Split> Delimited<(), S> {
540    /// Creates a new deserializer that can be used for [`WellKnown`] collections.
541    pub const fn new(sep: S) -> Self {
542        Self((), sep)
543    }
544}
545
546impl<De, S: Split> Delimited<Repeated<De>, S> {
547    /// Shortcut to wrap a [`Repeated`] deserializer.
548    pub const fn repeat(item_de: De, sep: S) -> Self {
549        Self(Repeated(item_de), sep)
550    }
551}
552
553impl<T, De, S> DeserializeParam<T> for Delimited<De, S>
554where
555    De: DeserializeParam<T>,
556    S: Split,
557{
558    const EXPECTING: BasicTypes = {
559        let base = <De as DeserializeParam<T>>::EXPECTING;
560        assert!(
561            base.contains(BasicTypes::ARRAY),
562            "can only apply `Delimited` to types that support deserialization from array"
563        );
564        base.or(BasicTypes::STRING)
565    };
566
567    fn describe(&self, description: &mut TypeDescription) {
568        self.0.describe(description);
569        description.set_items_sep(self.1.display());
570    }
571
572    fn deserialize_param(
573        &self,
574        mut ctx: DeserializeContext<'_>,
575        param: &'static ParamMetadata,
576    ) -> Result<T, ErrorWithOrigin> {
577        let Some(WithOrigin {
578            inner: Value::String(s),
579            origin,
580        }) = ctx.current_value()
581        else {
582            return self.0.deserialize_param(ctx, param);
583        };
584
585        let array_origin = Arc::new(ValueOrigin::Synthetic {
586            source: origin.clone(),
587            transform: format!("{}-delimited string", self.1.display()),
588        });
589
590        let array_items = self.1.split(s.expose()).enumerate().map(|(i, part)| {
591            let item_origin = ValueOrigin::Path {
592                source: array_origin.clone(),
593                path: i.to_string(),
594            };
595            let part = if s.is_secret() {
596                StrValue::Secret(part.into())
597            } else {
598                StrValue::Plain(part.into())
599            };
600            WithOrigin::new(Value::String(part), Arc::new(item_origin))
601        });
602        let array = WithOrigin::new(Value::Array(array_items.collect()), array_origin);
603        self.0.deserialize_param(ctx.patched(&array), param)
604    }
605
606    fn serialize_param(&self, param: &T) -> serde_json::Value {
607        self.0.serialize_param(param)
608    }
609}
610
611/// Deserializer that supports either a map or an array of `{ key: _, value: _ }` tuples (with customizable
612/// key / value names). Created using [`Entries::named()`].
613///
614/// Unlike [`Entries`], [`NamedEntries`] doesn't require keys to be deserializable from strings (although
615/// if they don't, map inputs will not work).
616///
617/// # Examples
618///
619/// ```
620/// use std::{collections::HashMap, time::Duration};
621/// # use smart_config::{de::Entries, testing, DescribeConfig, DeserializeConfig};
622///
623/// #[derive(DescribeConfig, DeserializeConfig)]
624/// struct TestConfig {
625///     #[config(with = Entries::WELL_KNOWN.named("num", "value"))]
626///     entries: HashMap<u64, String>,
627///     /// Can also be used with "linear" containers with tuple items.
628///     #[config(with = Entries::WELL_KNOWN.named("method", "timeout"))]
629///     tuples: Vec<(String, Duration)>,
630/// }
631///
632/// // Parsing from maps:
633/// let map_input = smart_config::config!(
634///     "entries": serde_json::json!({ "2": "two", "3": "three" }),
635///     "tuples": serde_json::json!({ "getLogs": "2s" }),
636/// );
637/// let config: TestConfig = testing::test(map_input)?;
638/// assert_eq!(
639///     config.entries,
640///     HashMap::from([(2, "two".to_owned()), (3, "three".to_owned())])
641/// );
642/// assert_eq!(
643///     config.tuples,
644///    [("getLogs".to_owned(), Duration::from_secs(2))]
645/// );
646///
647/// // The equivalent input as named tuples:
648/// let tuples_input = smart_config::config!(
649///     "entries": serde_json::json!([
650///         { "num": 2, "value": "two" },
651///         { "num": 3, "value": "three" },
652///     ]),
653///     "tuples": serde_json::json!([
654///         { "method": "getLogs", "timeout": "2s" },
655///     ]),
656/// );
657/// let config: TestConfig = testing::test(tuples_input)?;
658/// # assert_eq!(
659/// #     config.entries,
660/// #     HashMap::from([(2, "two".to_owned()), (3, "three".to_owned())])
661/// # );
662/// # assert_eq!(
663/// #     config.tuples,
664/// #    [("getLogs".to_owned(), Duration::from_secs(2))]
665/// # );
666/// # anyhow::Ok(())
667/// ```
668pub struct NamedEntries<
669    K,
670    V,
671    DeK = <K as WellKnown>::Deserializer,
672    DeV = <V as WellKnown>::Deserializer,
673> {
674    inner: Entries<K, V, DeK, DeV>,
675    keys_name: &'static str,
676    values_name: &'static str,
677}
678
679impl<K, V, DeK: fmt::Debug, DeV: fmt::Debug> fmt::Debug for NamedEntries<K, V, DeK, DeV> {
680    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
681        formatter
682            .debug_struct("NamedEntries")
683            .field("inner", &self.inner)
684            .field("keys_name", &self.keys_name)
685            .field("values_name", &self.values_name)
686            .finish()
687    }
688}
689
690impl<K, V, DeK, DeV> NamedEntries<K, V, DeK, DeV>
691where
692    DeK: DeserializeParam<K>,
693    DeV: DeserializeParam<V>,
694{
695    fn deserialize_entries<C: FromIterator<(K, V)>>(
696        &self,
697        mut ctx: DeserializeContext<'_>,
698        param: &'static ParamMetadata,
699        array: &[WithOrigin],
700        array_origin: &Arc<ValueOrigin>,
701    ) -> Result<C, ErrorWithOrigin> {
702        let mut has_errors = false;
703        let items = array.iter().enumerate().filter_map(|(i, entry)| {
704            let idx_str = i.to_string();
705            let (key, value) = match self.parse_entry(entry) {
706                Ok(entry) => entry,
707                Err(err) => {
708                    ctx.child(&idx_str, ctx.location_in_config).push_error(err);
709                    has_errors = true;
710                    return None;
711                }
712            };
713
714            let parsed_key =
715                parse_key_or_value::<K, _>(&mut ctx, param, &idx_str, &self.inner.keys, key);
716            let null_value;
717            let value = if let Some(value) = value {
718                value
719            } else {
720                let null_origin = ValueOrigin::Synthetic {
721                    source: entry.origin.clone(),
722                    transform: "missing entry value".to_owned(),
723                };
724                null_value = WithOrigin::new(Value::Null, Arc::new(null_origin));
725                &null_value
726            };
727            let parsed_value =
728                parse_key_or_value::<V, _>(&mut ctx, param, &idx_str, &self.inner.values, value);
729            has_errors |= parsed_key.is_none() || parsed_value.is_none();
730            Some((parsed_key?, parsed_value?)).filter(|_| !has_errors)
731        });
732        let items: C = items.collect();
733
734        if has_errors {
735            let origin = array_origin.clone();
736            Err(ErrorWithOrigin::new(LowLevelError::InvalidArray, origin))
737        } else {
738            Ok(items)
739        }
740    }
741
742    fn parse_entry<'a>(
743        &self,
744        entry: &'a WithOrigin,
745    ) -> Result<(&'a WithOrigin, Option<&'a WithOrigin>), ErrorWithOrigin> {
746        let Value::Object(obj) = &entry.inner else {
747            let expected = format!(
748                "{{ {:?}: _, {:?}: _ }} tuple",
749                self.keys_name, self.values_name
750            );
751            return Err(entry.invalid_type(&expected));
752        };
753
754        let key = obj.get(self.keys_name).ok_or_else(|| {
755            let err = DeError::missing_field(self.keys_name);
756            ErrorWithOrigin::json(err, entry.origin.clone())
757        })?;
758        let value = obj.get(self.values_name);
759
760        if obj.len() > 2 {
761            let expected = format!(
762                "{{ {:?}: _, {:?}: _ }} tuple",
763                self.keys_name, self.values_name
764            );
765            return Err(entry.invalid_type(&expected));
766        }
767        Ok((key, value))
768    }
769}
770
771impl<K, V, DeK, DeV, C> DeserializeParam<C> for NamedEntries<K, V, DeK, DeV>
772where
773    K: 'static,
774    V: 'static,
775    DeK: DeserializeParam<K>,
776    DeV: DeserializeParam<V>,
777    C: FromIterator<(K, V)> + ToEntries<K, V>,
778{
779    const EXPECTING: BasicTypes = BasicTypes::OBJECT.or(BasicTypes::ARRAY);
780
781    fn describe(&self, description: &mut TypeDescription) {
782        let details = format!(
783            "map or array of {{ {:?}: _, {:?}: _ }} tuples",
784            self.keys_name, self.values_name
785        );
786        description
787            .set_details(details)
788            .set_entries(&self.inner.keys, &self.inner.values);
789    }
790
791    fn deserialize_param(
792        &self,
793        ctx: DeserializeContext<'_>,
794        param: &'static ParamMetadata,
795    ) -> Result<C, ErrorWithOrigin> {
796        let deserializer = ctx.current_value_deserializer(param.name)?;
797        match deserializer.value() {
798            Value::Object(map) => {
799                let map_entries = map
800                    .iter()
801                    .map(|(key, value)| (key.as_str(), Cow::Borrowed(value)));
802                self.inner
803                    .deserialize_map(ctx, param, map_entries, deserializer.origin())
804            }
805            Value::Array(array) => {
806                self.deserialize_entries(ctx, param, array, deserializer.origin())
807            }
808            _ => Err(deserializer.invalid_type("object or array")),
809        }
810    }
811
812    fn serialize_param(&self, param: &C) -> serde_json::Value {
813        let entries = param.to_entries().map(|(key, value)| {
814            let key = self.inner.keys.serialize_param(key);
815            let value = self.inner.values.serialize_param(value);
816            serde_json::json!({
817                self.keys_name: key,
818                self.values_name: value,
819            })
820        });
821        serde_json::Value::Array(entries.collect())
822    }
823}
824
825/// Delimited variant of [`Entries`] that can deserialize a map from a delimited string
826/// (with addition to the conventional object deserialization).
827/// Can be constructed using [`Entries::delimited()`].
828///
829/// `DelimitedEntries` are characterized by 2 separators: one between entries, and another between
830/// key and value.
831///
832/// # Examples
833///
834/// ```
835/// # use std::collections::HashMap;
836/// use smart_config::{de, testing, DescribeConfig, DeserializeConfig};
837///
838/// #[derive(DescribeConfig, DeserializeConfig)]
839/// struct TestConfig {
840///     #[config(with = de::Entries::WELL_KNOWN.delimited(",", "="))]
841///     map: HashMap<String, i64>,
842/// }
843///
844/// let config = smart_config::config!("map": "call=5,send=-3");
845/// let config: TestConfig = testing::test(config)?;
846/// assert_eq!(
847///     config.map,
848///     HashMap::from([("call".into(), 5), ("send".into(), -3)])
849/// );
850/// # anyhow::Ok(())
851/// ```
852///
853/// ## Regex separators
854///
855/// Similar configuration that allows for whitespace around separators.
856///
857/// ```
858/// # use std::collections::HashMap;
859/// # use smart_config::{de, testing, DescribeConfig, DeserializeConfig};
860/// use smart_config::pat::lazy_regex;
861///
862/// #[derive(DescribeConfig, DeserializeConfig)]
863/// struct TestConfig {
864///     #[config(
865///         with = de::Entries::WELL_KNOWN.delimited(
866///             // see `lazy_regex!` docs for the syntax explanation
867///             lazy_regex!(ref r"\s*,\s*"),
868///             lazy_regex!(ref r"\s*=\s*"),
869///         )
870///     )]
871///     map: HashMap<String, i64>,
872/// }
873///
874/// let config = smart_config::config!("map": "call = 5, send = -3");
875/// let config: TestConfig = testing::test(config)?;
876/// # assert_eq!(
877/// #     config.map,
878/// #     HashMap::from([("call".into(), 5), ("send".into(), -3)])
879/// # );
880/// # anyhow::Ok(())
881/// ```
882pub struct DelimitedEntries<
883    K,
884    V,
885    DeK = <K as WellKnown>::Deserializer,
886    DeV = <V as WellKnown>::Deserializer,
887    ESep = &'static str,
888    KvSep = &'static str,
889> {
890    entry_sep: ESep,
891    key_value_sep: KvSep,
892    inner: Entries<K, V, DeK, DeV>,
893}
894
895impl<K, V, DeK, DeV, ESep, KvSep> fmt::Debug for DelimitedEntries<K, V, DeK, DeV, ESep, KvSep>
896where
897    DeK: fmt::Debug,
898    DeV: fmt::Debug,
899    ESep: Split,
900    KvSep: Split,
901{
902    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
903        formatter
904            .debug_struct("DelimitedEntries")
905            .field("item_sep", &self.entry_sep.display())
906            .field("key_value_sep", &self.key_value_sep.display())
907            .field("inner", &self.inner)
908            .finish()
909    }
910}
911
912impl<K, V, DeK, DeV, ESep, KvSep, C> DeserializeParam<C>
913    for DelimitedEntries<K, V, DeK, DeV, ESep, KvSep>
914where
915    DeK: DeserializeParam<K>,
916    DeV: DeserializeParam<V>,
917    ESep: Split,
918    KvSep: Split,
919    C: FromIterator<(K, V)>,
920    Entries<K, V, DeK, DeV>: DeserializeParam<C>,
921{
922    const EXPECTING: BasicTypes = Entries::<K, V, DeK, DeV>::EXPECTING.or(BasicTypes::STRING);
923
924    fn describe(&self, description: &mut TypeDescription) {
925        self.inner.describe(description);
926        description.set_entries_sep(self.entry_sep.display(), self.key_value_sep.display());
927    }
928
929    fn deserialize_param(
930        &self,
931        mut ctx: DeserializeContext<'_>,
932        param: &'static ParamMetadata,
933    ) -> Result<C, ErrorWithOrigin> {
934        let Some(WithOrigin {
935            inner: Value::String(s),
936            origin,
937        }) = ctx.current_value()
938        else {
939            return self.inner.deserialize_param(ctx, param);
940        };
941
942        let entry_sep = &self.entry_sep;
943        let key_value_sep = &self.key_value_sep;
944        let map_origin = Arc::new(ValueOrigin::Synthetic {
945            source: origin.clone(),
946            transform: format!(
947                "{}-delimited entries separated by {}",
948                entry_sep.display(),
949                key_value_sep.display()
950            ),
951        });
952        let mut errors = vec![];
953
954        let map_entries = entry_sep
955            .split(s.expose())
956            .enumerate()
957            .filter_map(|(i, part)| {
958                let Some((key_str, value_str)) = key_value_sep.split_once(part) else {
959                    let key_origin = ValueOrigin::Path {
960                        source: map_origin.clone(),
961                        path: i.to_string(),
962                    };
963                    let err = DeError::custom(format!(
964                        "{} separator is missing",
965                        key_value_sep.display()
966                    ));
967                    let err = ErrorWithOrigin::json(err, Arc::new(key_origin));
968                    errors.push(err);
969                    return None;
970                };
971
972                let value_origin = ValueOrigin::Path {
973                    source: map_origin.clone(),
974                    path: format!("{i}.$value"),
975                };
976                let value_string = if s.is_secret() {
977                    StrValue::Secret(value_str.into())
978                } else {
979                    StrValue::Plain(value_str.into())
980                };
981                let value = WithOrigin::new(Value::String(value_string), Arc::new(value_origin));
982                Some((key_str, Cow::Owned(value)))
983            });
984
985        let mut output = self
986            .inner
987            .deserialize_map(ctx.borrow(), param, map_entries, &map_origin);
988        if output.is_ok() && !errors.is_empty() {
989            output = Err(ErrorWithOrigin::new(
990                LowLevelError::InvalidObject,
991                map_origin,
992            ));
993        }
994        for err in errors {
995            ctx.push_error(err);
996        }
997        output
998    }
999
1000    fn serialize_param(&self, param: &C) -> serde_json::Value {
1001        self.inner.serialize_param(param)
1002    }
1003}