1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
use syn::{Data, DeriveInput, Field, Fields, FieldsNamed};

pub struct CharybdisFields<'a> {
    named_fields: &'a FieldsNamed,
}

impl<'a> CharybdisFields<'a> {
    pub fn new(named_fields: &'a FieldsNamed) -> Self {
        Self { named_fields }
    }

    pub fn from_input(input: &'a DeriveInput) -> Self {
        match &input.data {
            Data::Struct(data) => match &data.fields {
                Fields::Named(named_fields) => Self { named_fields },
                _ => panic!("#[charybdis_model] works only for structs with named fields!"),
            },
            _ => panic!("#[charybdis_model] works only on structs!"),
        }
    }

    pub fn all_fields(&self) -> Vec<Field> {
        self.named_fields.named.iter().cloned().collect()
    }

    pub fn db_fields(&self) -> Vec<Field> {
        self.all_fields()
            .iter()
            .filter(|field| !has_ignore_attribute(field))
            .cloned()
            .collect()
    }
}

pub fn strip_charybdis_attributes(input: &mut DeriveInput) {
    if let Data::Struct(data_struct) = &mut input.data {
        if let Fields::Named(fields_named) = &mut data_struct.fields {
            for field in &mut fields_named.named {
                field.attrs.retain(|attr| !attr.path().is_ident("charybdis"));
            }
        }
    }
}

// check if it has #[charybdis(ignore)]
fn has_ignore_attribute(field: &Field) -> bool {
    field.attrs.iter().any(|attr| {
        if attr.path().is_ident("charybdis") {
            let maybe_ident: Result<syn::Ident, _> = attr.parse_args();
            if let Ok(ident) = maybe_ident {
                return ident == "ignore";
            }
        }
        false
    })
}