cgp_field/traits/
has_field.rs

1use core::marker::PhantomData;
2use core::ops::Deref;
3
4use cgp_component::UseContext;
5
6/**
7    The `HasField` trait is used to implement getter methods for a type that
8    derives the trait.
9
10    When a struct uses `#[derive(HasField)]`, the macro would generate a `HasField`
11    implementation for each field in the struct, with the field name becoming a
12    type-level string to be used in the generic `Tag` parameter.
13
14    ## Example
15
16    Given the following struct:
17
18    ```rust,ignore
19    #[derive(HasField)]
20    pub struct Person {
21        pub name: String,
22        pub age: u8,
23    }
24    ```
25
26    The macro would generate the following implementation:
27
28    ```rust,ignore
29    impl HasField<Symbol!("name")> for Person {
30        type Value = String;
31
32        fn get_field(&self, _tag: PhantomData<Symbol!("name")>) -> &Self::Value {
33            &self.name
34        }
35    }
36
37    impl HasField<Symbol!("age")> for Person {
38        type Value = u8;
39
40        fn get_field(&self, _tag: PhantomData<Symbol!("age")>) -> &Self::Value {
41            &self.age
42        }
43    }
44    ```
45*/
46#[diagnostic::on_unimplemented(
47    message = "HasField is not implemented for {Self} with the field: {Tag}",
48    note = "You need to add #[derive(HasField)] to {Self} with the given field present in the struct"
49)]
50pub trait HasField<Tag> {
51    type Value;
52
53    fn get_field(&self, _tag: PhantomData<Tag>) -> &Self::Value;
54}
55
56pub trait FieldGetter<Context, Tag> {
57    type Value;
58
59    fn get_field(context: &Context, _tag: PhantomData<Tag>) -> &Self::Value;
60}
61
62#[diagnostic::do_not_recommend]
63impl<Context, Tag, Target, Value> HasField<Tag> for Context
64where
65    Context: DerefMap<Target = Target>,
66    Target: HasField<Tag, Value = Value>,
67{
68    type Value = Value;
69
70    fn get_field(&self, tag: PhantomData<Tag>) -> &Self::Value {
71        self.map_deref(|context| context.get_field(tag))
72    }
73}
74
75impl<Context, Tag, Field> FieldGetter<Context, Tag> for UseContext
76where
77    Context: HasField<Tag, Value = Field>,
78{
79    type Value = Field;
80
81    fn get_field(context: &Context, _tag: PhantomData<Tag>) -> &Self::Value {
82        context.get_field(PhantomData)
83    }
84}
85
86/**
87   A helper trait to help organize the lifetime inference in Rust.
88   Without this, `Self::Target` would need to be `'static`, as Rust couldn't
89   infer the correct lifetime when calling `context.deref().get_field()`.
90*/
91trait DerefMap: Deref {
92    fn map_deref<T>(&self, mapper: impl for<'a> FnOnce(&'a Self::Target) -> &'a T) -> &T;
93}
94
95impl<Context> DerefMap for Context
96where
97    Context: Deref,
98{
99    fn map_deref<T>(&self, mapper: impl for<'a> FnOnce(&'a Self::Target) -> &'a T) -> &T {
100        mapper(self.deref())
101    }
102}