ormlite_attr/metadata/
table.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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use crate::metadata::column::ColumnMeta;
use crate::DeriveInputExt;
use crate::Ident;
use convert_case::{Case, Casing};
use structmeta::StructMeta;
use syn::{Attribute, DeriveInput, LitStr};

/// Metadata used for IntoArguments, TableMeta, and (subset of) Model
/// This structs are constructed from the *Attribute structs in crate::attr.
#[derive(Debug, Clone)]
pub struct TableMeta {
    pub name: String,
    pub ident: Ident,
    pub columns: Vec<ColumnMeta>,
    pub databases: Vec<String>,

    /// If you're using this, consider whether you should be using a ModelMetadata and its pkey,
    /// which is not optional, instead.
    pub pkey: Option<String>,
}

impl TableMeta {
    pub fn new(ast: &DeriveInput, attrs: &[TableAttr]) -> Self {
        let ident = &ast.ident;
        let name = if let Some(value) = attrs.iter().find_map(|a| a.table.as_ref()) {
            value.value()
        } else {
            ident.to_string().to_case(Case::Snake)
        };
        let mut columns = ColumnMeta::from_fields(ast.fields());
        let mut pkey = columns
            .iter()
            .find(|&c| c.marked_primary_key)
            .map(|c| c.clone())
            .map(|c| c.name.clone());
        if pkey.is_none() {
            let candidates = sqlmo::util::pkey_column_names(&name);
            if let Some(c) = columns.iter_mut().find(|c| candidates.iter().any(|n| c.ident == n)) {
                c.has_database_default = true;
                pkey = Some(c.name.clone());
            }
        }
        let databases = attrs.iter().flat_map(|d| &d.database).map(|d| d.value()).collect();
        Self {
            name,
            ident: Ident::from(ident),
            columns,
            databases,
            pkey,
        }
    }

    pub fn from_derive(ast: &DeriveInput) -> Self {
        let attr = TableAttr::from_attrs(&ast.attrs);
        Self::new(ast, &attr)
    }

    pub fn all_fields(&self) -> impl Iterator<Item = &Ident> + '_ {
        self.columns.iter().map(|c| &c.ident)
    }

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

    pub fn many_to_one_joins(&self) -> impl Iterator<Item = &ColumnMeta> + '_ {
        self.columns.iter().filter(|&c| c.many_to_one_column_name.is_some())
    }

    #[allow(dead_code)]
    pub(crate) fn mock(name: &str, columns: Vec<ColumnMeta>) -> Self {
        TableMeta {
            name: name.to_string(),
            ident: Ident::from(name.to_case(Case::Pascal)),
            pkey: None,
            columns,
            databases: vec![],
        }
    }
}

/// Available attributes on a struct
#[derive(StructMeta)]
pub struct TableAttr {
    /// The name of the table in the database. Defaults to the struct name.
    /// Example:
    /// #[ormlite(table = "users")]
    /// pub struct User {
    ///    pub id: i32,
    /// }
    pub table: Option<LitStr>,

    /// Deprecated name for insert
    /// Used as `#[ormlite(insertable = InsertUser)]`
    pub insertable: Option<syn::Ident>,

    /// The struct name of an insertion struct.
    /// Example:
    /// #[ormlite(insert = "InsertUser")]
    /// pub struct User {
    ///   pub id: i32,
    /// }
    ///
    pub insert: Option<LitStr>,

    /// Add extra derives to the insertion structs.
    /// Example:
    /// #[ormlite(insert = "InsertUser", extra_derives(Serialize, Deserialize))]
    /// pub struct User {
    ///   pub id: i32,
    /// }
    ///
    pub extra_derives: Option<Vec<syn::Ident>>,

    /// Only used for derive(Insert)
    /// Example:
    /// #[ormlite(returns = "User")]
    /// pub struct InsertUser {}
    pub returns: Option<LitStr>,

    /// Set the target database. Only needed if you have multiple databases enabled.
    /// If you have a single database enabled, you don't need to set this.
    /// Even with multiple databases, you can skip this by setting a default database with the `default-<db>` feature.
    ///
    /// Currently, because methods conflict, you
    /// You can use this attribute multiple times to set multiple databases.
    /// Example:
    /// #[ormlite(database = "postgres")]
    /// #[ormlite(database = "sqlite")]
    /// pub struct User {
    ///  pub id: i32,
    /// }
    /// This will generate orm code for `User` for both the `postgres` and `sqlite` databases.
    pub database: Option<LitStr>,
}

impl TableAttr {
    pub fn from_attrs(attrs: &[Attribute]) -> Vec<Self> {
        attrs
            .iter()
            .filter(|&a| a.path().is_ident("ormlite"))
            .map(|a| a.parse_args().unwrap())
            .collect()
    }
}