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}