radix_rust/
resolve.rs

1use crate::prelude::*;
2
3/// This trait is intended to be used as an `impl` argument in helper methods, to accept
4/// a wider range of arguments.
5///
6/// It should only be used where it is safe to panic if the wrong argument is provided,
7/// and where performance isn't a primary concern.
8///
9/// It's not expected for other types to implement this trait directly - instead they
10/// should implement [`TryFrom`] to convert between the types.
11///
12/// If resolution needs to be keyed against an external resolver (e.g. a look-up to translate
13/// string names into values), then [`LabelledResolve`] should be used instead.
14///
15/// ## Implementers
16/// * You should prefer to implement [`ResolveFrom`] as it is easier to implement
17/// due to trait coherence rules. Sometimes you can only implement [`Resolve`]
18/// however.
19/// * If requiring a labelled resolution in your bounds, prefer [`Resolve`]
20/// because slightly more types can implement it.
21pub trait Resolve<X: Resolvable> {
22    fn resolve(self) -> X;
23}
24
25/// The inverse trait of [`Resolve`].
26///
27/// This should be implemented instead of [`Resolve`] where possible, but
28/// [`Resolve`] should be used as bounds in arguments.
29pub trait ResolveFrom<X>: Resolvable {
30    fn resolve_from(value: X) -> Self;
31}
32
33impl<X, Y: ResolveFrom<X>> Resolve<Y> for X {
34    fn resolve(self) -> Y {
35        Y::resolve_from(self)
36    }
37}
38
39/// `Resolvable` is a marker trait, mainly to make resolution opt-in and to avoid
40/// polluting every type with a resolve method.
41///
42/// You might want to use [`resolvable_with_identity_impl`] or [`resolvable_with_try_into_impls`]
43/// to implement this trait and a reflexive or blanket impl.
44pub trait Resolvable {}
45
46#[macro_export]
47macro_rules! resolvable_with_identity_impl {
48    ($ty:ty$(,)?) => {
49        impl Resolvable for $ty {}
50
51        impl ResolveFrom<$ty> for $ty {
52            fn resolve_from(value: $ty) -> $ty {
53                value
54            }
55        }
56    };
57}
58
59#[macro_export]
60macro_rules! resolvable_with_try_into_impls {
61    ($ty:ty$(,)?) => {
62        impl Resolvable for $ty {}
63
64        impl<T: TryInto<$ty, Error = E>, E: Debug> ResolveFrom<T> for $ty {
65            fn resolve_from(value: T) -> $ty {
66                value.try_into().unwrap_or_else(|err| {
67                    panic!(
68                        "The provided argument could not be resolved into a {}: {err:?}",
69                        core::any::type_name::<$ty>()
70                    )
71                })
72            }
73        }
74    };
75}
76
77impl<'a, X: ResolveFrom<X> + Clone> ResolveFrom<&'a X> for X {
78    fn resolve_from(value: &'a X) -> X {
79        value.clone()
80    }
81}
82
83/// This trait is intended to be used as an `impl` argument in helper methods, to accept
84/// a wider range of arguments.
85///
86/// It should only be used where it is safe to panic if the wrong argument is provided,
87/// and where performance isn't a primary concern.
88///
89/// Compared to [`Resolve`], [`LabelledResolve`] also accepts an optional resolver,
90/// which can be used to convert label/s either directly into `Self`, or into values which
91/// can be used to build up self.
92///
93/// However, unlike [`Resolve`], a reflexive [`LabelledResolve`] is only implemented for
94/// `Self`, `&Self` and various string labels. It doesn't build on top of [`TryInto`]
95/// because that causes implementation collisions with labels for types which could implement
96/// `TryFrom<&str>`.
97///
98/// ## Implementers
99/// * You should prefer to implement [`LabelledResolveFrom`] as it is easier to implement
100/// due to trait coherence rules. Sometimes you can only implement [`LabelledResolve`]
101/// however.
102/// * If requiring a labelled resolution in your bounds, prefer [`LabelledResolve`]
103/// because slightly more types can implement it.
104pub trait LabelledResolve<Y: LabelledResolvable> {
105    fn labelled_resolve(self, resolver: &impl LabelResolver<Y::ResolverOutput>) -> Y;
106}
107
108/// The inverse trait of [`LabelledResolve`].
109///
110/// This should be implemented instead of [`LabelledResolve`] where possible, but
111/// [`LabelledResolve`] should be used as bounds in arguments.
112pub trait LabelledResolveFrom<X>: LabelledResolvable {
113    fn labelled_resolve_from(value: X, resolver: &impl LabelResolver<Self::ResolverOutput>)
114        -> Self;
115}
116
117impl<X, Y: LabelledResolveFrom<X>> LabelledResolve<Y> for X {
118    fn labelled_resolve(
119        self,
120        resolver: &impl LabelResolver<<Y as LabelledResolvable>::ResolverOutput>,
121    ) -> Y {
122        Y::labelled_resolve_from(self, resolver)
123    }
124}
125
126/// `LabelledResolvable` is a marker trait, serving a few purposes:
127/// * It avoids polluting every type with a resolve method
128/// * It avoids trait definition collisions, by ensuring key types (e.g. &str) don't implement it.
129/// * It allows providing [`ResolverOutput`] to establish what kind of resolver it works with.
130///   This allows distinguishing "leaf" nodes which can be directly resolved from a resolver,
131///   and have [`ResolverOutput`] equal to `Self`, from container types (e.g. `Option` and `Vec`
132///   which don't have that bound).
133///
134/// If implementing this with [`ResolverOutput`] = `Self`, you will likely want to
135/// use [`labelled_resolvable_with_identity_impl`] or [`labelled_resolvable_with_try_into_impls`]
136/// to implement this trait and a reflexive or blanket impl using `try_into`.
137///
138/// [`ResolverOutput`]: LabelledResolvable::ResolverOutput
139pub trait LabelledResolvable {
140    /// You'll be passed a resolver, what will the resolver output?
141    /// Often this will be `Self`, but sometimes it will be another type which you will
142    /// need to map into `Self`.
143    type ResolverOutput;
144}
145
146pub trait LabelResolver<X> {
147    fn resolve_label_into(&self, label: &str) -> X;
148}
149
150#[macro_export]
151macro_rules! labelled_resolvable_with_identity_impl {
152    ($ty:ty, resolver_output: $resolver_output:ty$(,)?) => {
153        impl LabelledResolvable for $ty {
154            type ResolverOutput = $resolver_output;
155        }
156
157        impl LabelledResolveFrom<$ty> for $ty {
158            fn labelled_resolve_from(
159                value: Self,
160                _resolver: &impl LabelResolver<$resolver_output>,
161            ) -> Self {
162                value
163            }
164        }
165
166        // In future, could likely add an implementation from &$ty if $ty is Clone;
167        // if we can get around the "trivially true/false" bound.
168    };
169}
170
171#[macro_export]
172macro_rules! labelled_resolvable_using_resolvable_impl {
173    ($ty:ty, resolver_output: $resolver_output:ty$(,)?) => {
174        impl LabelledResolvable for $ty {
175            type ResolverOutput = $resolver_output;
176        }
177
178        impl<T: Resolve<Self>> LabelledResolveFrom<T> for $ty {
179            fn labelled_resolve_from(
180                value: T,
181                _resolver: &impl LabelResolver<$resolver_output>,
182            ) -> Self {
183                value.resolve()
184            }
185        }
186    };
187}
188
189//==============================================================
190// If a type `X` has `ResolverOutput = Self` then it's a "leaf" - i.e. the thing
191// that's ultimately being resolved.
192// * We leave an identity resolver or try_into resolver for the macros `labelled_resolvable_with_identity_impl`
193//   or `labelled_resolvable_with_try_into_impls` or `labelled_resolvable_using_resolvable_impl`
194// * Implement resolves form string-based labels
195//==============================================================
196
197// Ideally we'd be able to allow `ResolverOutput = TryInfo<X>`, but the
198// compiler disallows this, due to clashes with other blanket implementations.
199// For example, it might be possible in future for e.g. &'a str to implement
200// `IntoIterator<Item = A>` (e.g. A = &'a char) and for `A` to implement
201// `Resolve<X>` and so give clashing implementations of
202// LabelledResolveFrom<&'a str> for Vec<char>.
203
204impl<'a, X: LabelledResolvable<ResolverOutput = X>> LabelledResolveFrom<&'a str> for X {
205    fn labelled_resolve_from(value: &'a str, resolver: &impl LabelResolver<X>) -> X {
206        resolver.resolve_label_into(value)
207    }
208}
209
210impl<'a, X: LabelledResolvable<ResolverOutput = X>> LabelledResolveFrom<&'a String> for X {
211    fn labelled_resolve_from(value: &'a String, resolver: &impl LabelResolver<X>) -> X {
212        resolver.resolve_label_into(value.as_str())
213    }
214}
215
216impl<X: LabelledResolvable<ResolverOutput = X>> LabelledResolveFrom<String> for X {
217    fn labelled_resolve_from(value: String, resolver: &impl LabelResolver<X>) -> X {
218        resolver.resolve_label_into(value.as_str())
219    }
220}
221
222//==============================================================
223// Handle Option<X>
224//==============================================================
225// - None and Some(X) are handled by the identity above
226// - We then handle label -> Some(X) below
227//==============================================================
228
229impl<X: LabelledResolvable> LabelledResolvable for Option<X> {
230    type ResolverOutput = X;
231}
232
233impl<X: LabelledResolvable> LabelledResolveFrom<Option<X>> for Option<X> {
234    fn labelled_resolve_from(value: Option<X>, _resolver: &impl LabelResolver<X>) -> Option<X> {
235        value
236    }
237}
238
239impl<X: LabelledResolvable> LabelledResolveFrom<X> for Option<X> {
240    fn labelled_resolve_from(value: X, _resolver: &impl LabelResolver<X>) -> Option<X> {
241        Some(value)
242    }
243}
244
245impl<'a, X: LabelledResolvable + Clone> LabelledResolveFrom<&'a X> for Option<X> {
246    fn labelled_resolve_from(value: &'a X, _resolver: &impl LabelResolver<X>) -> Option<X> {
247        Some(value.clone())
248    }
249}
250
251impl<'a, X: LabelledResolvable + Clone> LabelledResolveFrom<&'a Option<X>> for Option<X> {
252    fn labelled_resolve_from(value: &'a Option<X>, _resolver: &impl LabelResolver<X>) -> Option<X> {
253        value.clone()
254    }
255}
256
257impl<'a, X: LabelledResolvable> LabelledResolveFrom<&'a str> for Option<X> {
258    fn labelled_resolve_from(value: &'a str, resolver: &impl LabelResolver<X>) -> Option<X> {
259        Some(resolver.resolve_label_into(value))
260    }
261}
262
263impl<'a, X: LabelledResolvable> LabelledResolveFrom<&'a String> for Option<X> {
264    fn labelled_resolve_from(value: &'a String, resolver: &impl LabelResolver<X>) -> Option<X> {
265        Some(resolver.resolve_label_into(value.as_str()))
266    }
267}
268
269impl<'a, X: LabelledResolvable> LabelledResolveFrom<String> for Option<X> {
270    fn labelled_resolve_from(value: String, resolver: &impl LabelResolver<X>) -> Option<X> {
271        Some(resolver.resolve_label_into(value.as_str()))
272    }
273}
274
275//==============================================================
276// Handle collections
277//==============================================================
278// - An iterator over something that resolves to X, resolves to
279//   the given collection/s of X.
280// Feel free to add more collections here as needed.
281//==============================================================
282
283impl<X: LabelledResolvable> LabelledResolvable for Vec<X> {
284    type ResolverOutput = X;
285}
286
287impl<T, X> LabelledResolveFrom<T> for Vec<X>
288where
289    T: IntoIterator,
290    T::Item: LabelledResolve<X>,
291    X: LabelledResolvable<ResolverOutput = X>,
292{
293    fn labelled_resolve_from(value: T, resolver: &impl LabelResolver<X>) -> Vec<X> {
294        value
295            .into_iter()
296            .map(|item| LabelledResolve::<X>::labelled_resolve(item, resolver))
297            .collect()
298    }
299}
300
301impl<X: LabelledResolvable> LabelledResolvable for IndexSet<X> {
302    type ResolverOutput = X;
303}
304
305impl<T, X> LabelledResolveFrom<T> for IndexSet<X>
306where
307    T: IntoIterator,
308    T::Item: LabelledResolve<X>,
309    X: LabelledResolvable<ResolverOutput = X> + core::hash::Hash + core::cmp::Eq,
310{
311    fn labelled_resolve_from(value: T, resolver: &impl LabelResolver<X>) -> IndexSet<X> {
312        value
313            .into_iter()
314            .map(|item| LabelledResolve::<X>::labelled_resolve(item, resolver))
315            .collect()
316    }
317}