cgp_field/traits/
has_field.rs

1use core::marker::PhantomData;
2use core::ops::Deref;
3
4use cgp_component::UseContext;
5
6#[diagnostic::on_unimplemented(
7    message = "HasField is not implemented for {Self} with the field: {Tag}",
8    note = "You need to add #[derive(HasField)] to {Self} with the given field present in the struct"
9)]
10pub trait HasField<Tag> {
11    type Value;
12
13    fn get_field(&self, _tag: PhantomData<Tag>) -> &Self::Value;
14}
15
16pub trait FieldGetter<Context, Tag> {
17    type Value;
18
19    fn get_field(context: &Context, _tag: PhantomData<Tag>) -> &Self::Value;
20}
21
22#[diagnostic::do_not_recommend]
23impl<Context, Tag, Target, Value> HasField<Tag> for Context
24where
25    Context: DerefMap<Target = Target>,
26    Target: HasField<Tag, Value = Value>,
27{
28    type Value = Value;
29
30    fn get_field(&self, tag: PhantomData<Tag>) -> &Self::Value {
31        self.map_deref(|context| context.get_field(tag))
32    }
33}
34
35impl<Context, Tag, Field> FieldGetter<Context, Tag> for UseContext
36where
37    Context: HasField<Tag, Value = Field>,
38{
39    type Value = Field;
40
41    fn get_field(context: &Context, _tag: PhantomData<Tag>) -> &Self::Value {
42        context.get_field(PhantomData)
43    }
44}
45
46/**
47   A helper trait to help organize the lifetime inference in Rust.
48   Without this, `Self::Target` would need to be `'static`, as Rust couldn't
49   infer the correct lifetime when calling `context.deref().get_field()`.
50*/
51trait DerefMap: Deref {
52    fn map_deref<T>(&self, mapper: impl for<'a> FnOnce(&'a Self::Target) -> &'a T) -> &T;
53}
54
55impl<Context> DerefMap for Context
56where
57    Context: Deref,
58{
59    fn map_deref<T>(&self, mapper: impl for<'a> FnOnce(&'a Self::Target) -> &'a T) -> &T {
60        mapper(self.deref())
61    }
62}