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()
}
}