ormlite_attr/metadata/
model.rs

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use crate::metadata::column::ColumnMeta;
use crate::metadata::table::TableMeta;
use crate::Ident;
use crate::TableAttr;
use syn::DeriveInput;

/// Metadata used for IntoArguments, TableMeta, and (subset of) Model
#[derive(Debug, Clone)]
pub struct ModelMeta {
    pub table: TableMeta,
    pub insert_struct: Option<Ident>,
    pub extra_derives: Option<Vec<Ident>>,
    pub pkey: ColumnMeta,
}

impl ModelMeta {
    pub fn builder_struct(&self) -> Ident {
        Ident::from(format!("{}Builder", self.ident.as_ref()))
    }

    pub fn database_columns_except_pkey(&self) -> impl Iterator<Item = &ColumnMeta> + '_ {
        self.columns
            .iter()
            .filter(|&c| !c.skip)
            .filter(|&c| self.pkey.name != c.name)
    }

    pub fn from_derive(ast: &DeriveInput) -> Self {
        let attrs = TableAttr::from_attrs(&ast.attrs);
        let table = TableMeta::new(ast, &attrs);
        let pkey = table.pkey.as_deref().expect(&format!(
            "No column marked with #[ormlite(primary_key)], and no column named id, uuid, {0}_id, or {0}_uuid",
            table.name,
        ));
        let mut insert_struct = None;
        let mut extra_derives: Option<Vec<syn::Ident>> = None;
        for attr in attrs {
            if let Some(v) = attr.insert {
                insert_struct = Some(v.value());
            }
            if let Some(v) = attr.insertable {
                insert_struct = Some(v.to_string());
            }
            if let Some(v) = attr.extra_derives {
                if !v.is_empty() {
                    extra_derives = Some(v);
                }
            }
        }
        let pkey = table.columns.iter().find(|&c| c.name == pkey).unwrap().clone();
        let insert_struct = insert_struct.map(|v| Ident::from(v));
        let extra_derives = extra_derives.take().map(|vec| vec.into_iter().map(|v| v.to_string()).map(Ident::from).collect());
        
        Self {
            table,
            insert_struct,
            extra_derives, 
            pkey,
        }
    }

    #[doc(hidden)]
    pub fn mock(name: &str, columns: Vec<ColumnMeta>) -> Self {
        let inner = TableMeta::mock(name, columns);
        Self {
            pkey: inner.columns.iter().find(|c| c.name == "id").unwrap().clone(),
            table: inner,
            extra_derives: None,
            insert_struct: None,
        }
    }
}

impl std::ops::Deref for ModelMeta {
    type Target = TableMeta;

    fn deref(&self) -> &Self::Target {
        &self.table
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use syn::ItemStruct;

    #[test]
    fn test_decode_metadata() {
        let ast = syn::parse_str::<ItemStruct>(
            r#"struct User {
            #[ormlite(column = "Id")]
            id: i32,
        }"#,
        )
        .unwrap();
        let input = DeriveInput::from(ast);
        let meta = ModelMeta::from_derive(&input);
        assert_eq!(meta.pkey.name, "Id");
    }
}