glossa_dsl/resolver/
from_slice.rs

1use alloc::collections::BTreeMap;
2
3use tap::Pipe;
4
5use crate::{
6  Resolver,
7  error::{ResolverError, ResolverResult},
8  parsers::parse_value_or_map_err,
9  resolver::AST,
10};
11
12impl TryFrom<&[(&str, &str)]> for Resolver {
13  type Error = ResolverError;
14
15  fn try_from(value: &[(&str, &str)]) -> Result<Self, Self::Error> {
16    Self::try_from_slice(value)
17  }
18}
19
20impl<const N: usize> TryFrom<[(&str, &str); N]> for Resolver {
21  type Error = ResolverError;
22
23  fn try_from(value: [(&str, &str); N]) -> Result<Self, Self::Error> {
24    Self::try_from_str_entries(value.into_iter())
25  }
26}
27
28impl<K, V> TryFrom<BTreeMap<K, V>> for Resolver
29where
30  K: AsRef<str>,
31  V: AsRef<str>,
32{
33  type Error = ResolverError;
34
35  fn try_from(value: BTreeMap<K, V>) -> Result<Self, Self::Error> {
36    Self::try_from_str_entries(value.into_iter())
37  }
38}
39
40impl Resolver {
41  /// Construct from slice (no_std compatible)
42  ///
43  /// ```
44  /// use tap::Pipe;
45  /// use glossa_dsl::Resolver;
46  ///
47  /// let res = [
48  ///   ("🐱", "喵 ฅ(°ω°ฅ)"),
49  ///   ("hello", "Hello {🐱}"),
50  /// ]
51  ///  .as_ref()
52  ///  .pipe(Resolver::try_from_slice)?;
53  ///
54  /// let text = res.get_with_context("hello", &[])?;
55  /// assert_eq!(text, "Hello 喵 ฅ(°ω°ฅ)");
56  ///
57  /// # Ok::<(), glossa_dsl::error::ResolverError>(())
58  /// ```
59  pub fn try_from_slice(raw: &[(&str, &str)]) -> ResolverResult<Self> {
60    Self::try_from_str_entries(raw.iter().copied())
61  }
62
63  /// Attempts to build a Resolver from raw unprocessed key-value
64  /// entries.
65  ///
66  /// ## Process Flow
67  ///
68  /// 1. Accepts an iterator of raw (key, value) pairs
69  /// 2. Parses each value into template AST (Abstract Syntax Tree)
70  /// 3. Converts keys to normalized format
71  /// 4. Collects results into a Glossa-DSL AST
72  /// 5. Constructs the final resolver
73  ///
74  /// ## Parameters
75  /// - `iter`: Iterator over raw unvalidated entries.
76  ///   - e.g., `[(k1, v1), (k2, v2)].into_iter()`
77  ///
78  /// ## Type Constraints
79  /// - `K`: Key type with string-like representation
80  /// - `V`: Raw value type containing template text
81  /// - `I`: Iterator providing raw configuration entries
82  ///
83  /// ## Example
84  ///
85  /// ```
86  /// # #[cfg(all(feature = "serde", feature = "toml"))] {
87  /// use tap::Pipe;
88  /// use glossa_dsl::{Resolver, resolver::MiniStr, resolver::BTreeRawMap};
89  ///
90  ///
91  /// let res = r##"
92  ///   meow = "喵"
93  ///   "🐱" = "{ meow } ฅ(°ω°ฅ)"
94  /// "##
95  ///   .pipe(toml::from_str::<BTreeRawMap>)?
96  ///   .into_iter()
97  ///   .pipe(Resolver::try_from_str_entries)?;
98  ///
99  /// assert_eq!(res.try_get("🐱")?, "喵 ฅ(°ω°ฅ)");
100  ///
101  /// # }
102  /// # Ok::<(), glossa_dsl::Error>(())
103  /// ```
104  ///
105  /// See also:
106  ///   - [Self::try_from_slice]
107  ///   - [Self::try_from_raw]
108  pub fn try_from_str_entries<K, V, I>(iter: I) -> ResolverResult<Self>
109  where
110    K: AsRef<str>,
111    V: AsRef<str>,
112    I: Iterator<Item = (K, V)>,
113  {
114    iter
115      .map(|(key, value)| {
116        parse_value_or_map_err(key.as_ref(), value.as_ref()) //
117          .map(|tmpl| (convert_map_key(key.as_ref()), tmpl))
118      })
119      .collect::<Result<AST, _>>()?
120      .pipe(Self)
121      .pipe(Ok)
122  }
123}
124
125#[cfg(not(feature = "std"))]
126fn convert_map_key(key: &str) -> crate::MiniStr {
127  key.into()
128}
129
130#[cfg(feature = "std")]
131fn convert_map_key(key: &str) -> kstring::KString {
132  key.pipe(kstring::KString::from_ref)
133}
134
135#[cfg(test)]
136mod tests {
137  use super::*;
138
139  #[ignore]
140  #[test]
141  fn test_try_from_slice() -> ResolverResult<()> {
142    let _res = [
143      ("g", "Good"),
144      ("greeting", "{g} { time-period }! { $name }"),
145      (
146        "time-period",
147        "$period ->
148          [morning] Morning
149          *[other] {$period}",
150      ),
151    ]
152    .pipe_as_ref(Resolver::try_from_slice)?;
153
154    // extern crate std;
155    // std::dbg!(res);
156    Ok(())
157  }
158}