ormlite_attr/metadata/
table.rs

1use crate::metadata::column::ColumnMeta;
2use crate::DeriveInputExt;
3use crate::Ident;
4use convert_case::{Case, Casing};
5use structmeta::StructMeta;
6use syn::{Attribute, DeriveInput, LitStr};
7
8/// Metadata used for IntoArguments, TableMeta, and (subset of) Model
9/// This structs are constructed from the *Attribute structs in crate::attr.
10#[derive(Debug, Clone)]
11pub struct TableMeta {
12    pub name: String,
13    pub ident: Ident,
14    pub columns: Vec<ColumnMeta>,
15    pub databases: Vec<String>,
16
17    /// If you're using this, consider whether you should be using a ModelMetadata and its pkey,
18    /// which is not optional, instead.
19    pub pkey: Option<String>,
20}
21
22impl TableMeta {
23    pub fn new(ast: &DeriveInput, attrs: &[TableAttr]) -> Self {
24        let ident = &ast.ident;
25        let name = if let Some(value) = attrs.iter().find_map(|a| a.table.as_ref()) {
26            value.value()
27        } else {
28            ident.to_string().to_case(Case::Snake)
29        };
30        let mut columns = ColumnMeta::from_fields(ast.fields());
31        let mut pkey = columns
32            .iter()
33            .find(|&c| c.marked_primary_key)
34            .map(|c| c.clone())
35            .map(|c| c.name.clone());
36        if pkey.is_none() {
37            let candidates = sql::util::pkey_column_names(&name);
38            if let Some(c) = columns.iter_mut().find(|c| candidates.iter().any(|n| c.ident == n)) {
39                c.has_database_default = true;
40                pkey = Some(c.name.clone());
41            }
42        }
43        let databases = attrs.iter().flat_map(|d| &d.database).map(|d| d.value()).collect();
44        Self {
45            name,
46            ident: Ident::from(ident),
47            columns,
48            databases,
49            pkey,
50        }
51    }
52
53    pub fn from_derive(ast: &DeriveInput) -> Self {
54        let attr = TableAttr::from_attrs(&ast.attrs);
55        Self::new(ast, &attr)
56    }
57
58    pub fn all_fields(&self) -> impl Iterator<Item = &Ident> + '_ {
59        self.columns.iter().map(|c| &c.ident)
60    }
61
62    pub fn database_columns(&self) -> impl Iterator<Item = &ColumnMeta> + '_ {
63        self.columns
64            .iter()
65            .filter(|&c| !c.skip)
66            .filter(|&c| !c.is_join() || c.is_join_one())
67    }
68
69    pub fn many_to_one_joins(&self) -> impl Iterator<Item = &ColumnMeta> + '_ {
70        self.columns.iter().filter(|&c| c.is_join_one())
71    }
72
73    #[allow(dead_code)]
74    pub(crate) fn mock(name: &str, columns: Vec<ColumnMeta>) -> Self {
75        TableMeta {
76            name: name.to_string(),
77            ident: Ident::from(name.to_case(Case::Pascal)),
78            pkey: None,
79            columns,
80            databases: vec![],
81        }
82    }
83}
84
85/// Available attributes on a struct
86#[derive(StructMeta)]
87pub struct TableAttr {
88    /// The name of the table in the database. Defaults to the struct name.
89    /// Example:
90    /// #[ormlite(table = "users")]
91    /// pub struct User {
92    ///    pub id: i32,
93    /// }
94    pub table: Option<LitStr>,
95
96    /// Deprecated name for insert
97    /// Used as `#[ormlite(insertable = InsertUser)]`
98    pub insertable: Option<syn::Ident>,
99
100    /// The struct name of an insertion struct.
101    /// Example:
102    /// #[ormlite(insert = "InsertUser")]
103    /// pub struct User {
104    ///   pub id: i32,
105    /// }
106    ///
107    pub insert: Option<LitStr>,
108
109    /// Add extra derives to the insertion structs.
110    /// Example:
111    /// #[ormlite(insert = "InsertUser", extra_derives(Serialize, Deserialize))]
112    /// pub struct User {
113    ///   pub id: i32,
114    /// }
115    ///
116    pub extra_derives: Option<Vec<syn::Ident>>,
117
118    /// Only used for derive(Insert)
119    /// Example:
120    /// #[ormlite(returns = "User")]
121    /// pub struct InsertUser {}
122    pub returns: Option<LitStr>,
123
124    /// Set the target database. Only needed if you have multiple databases enabled.
125    /// If you have a single database enabled, you don't need to set this.
126    /// Even with multiple databases, you can skip this by setting a default database with the `default-<db>` feature.
127    ///
128    /// Currently, because methods conflict, you
129    /// You can use this attribute multiple times to set multiple databases.
130    /// Example:
131    /// #[ormlite(database = "postgres")]
132    /// #[ormlite(database = "sqlite")]
133    /// pub struct User {
134    ///  pub id: i32,
135    /// }
136    /// This will generate orm code for `User` for both the `postgres` and `sqlite` databases.
137    pub database: Option<LitStr>,
138}
139
140impl TableAttr {
141    pub fn from_attrs(attrs: &[Attribute]) -> Vec<Self> {
142        attrs
143            .iter()
144            .filter(|&a| a.path().is_ident("ormlite"))
145            .map(|a| a.parse_args().unwrap())
146            .collect()
147    }
148}