Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions crates/toasty-codegen/src/expand/schema.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::{util, Expand};
use crate::schema::{ColumnType, FieldTy, Name};
use crate::schema::{Column, FieldTy, Name};

use proc_macro2::TokenStream;
use quote::quote;
Expand Down Expand Up @@ -50,18 +50,30 @@ impl Expand<'_> {

let fields = self.model.fields.iter().enumerate().map(|(index, field)| {
let index_tokenized = util::int(index);
let name = field.name.ident.to_string();
let field_ty;
let nullable;

let name = {
let app_name = field.name.ident.to_string();
let storage_name = match field.attrs.column.as_ref().and_then(|column| column.name.as_ref()) {
Some(name) => quote! { Some(#name.to_string()) },
None => quote! { None },
};
quote! {
FieldName {
app_name: #app_name.to_string(),
storage_name: #storage_name,
}
}
};

match &field.ty {
FieldTy::Primitive(ty) => {
let storage_ty = match &field.attrs.db {
Some(ColumnType::VarChar(size)) => {
let size = util::int(*size);
quote!(Some(db::Type::VarChar(#size)))
let storage_ty = match &field.attrs.column {
Some(Column { ty: Some(ty), ..}) => {
quote!(Some(#ty))
}
None => quote!(None),
_ => quote!(None),
};

nullable = quote!(<#ty as #toasty::stmt::Primitive>::NULLABLE);
Expand Down Expand Up @@ -144,7 +156,7 @@ impl Expand<'_> {
model: #model_ident::id(),
index: #index_tokenized,
},
name: #name.to_string(),
name: #name,
ty: #field_ty,
nullable: #nullable,
primary_key: #primary_key,
Expand Down
4 changes: 2 additions & 2 deletions crates/toasty-codegen/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ pub(crate) use name::Name;
mod pk;
pub(crate) use pk::PrimaryKey;

mod ty;
pub(crate) use ty::ColumnType;
mod column;
pub(crate) use column::Column;
95 changes: 95 additions & 0 deletions crates/toasty-codegen/src/schema/column.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use quote::quote;
use syn::parenthesized;

#[derive(Debug)]
pub(crate) struct Column {
pub(crate) name: Option<syn::LitStr>,
pub(crate) ty: Option<ColumnType>,
}

impl Column {
pub(super) fn from_ast(attr: &syn::Attribute) -> syn::Result<Column> {
attr.parse_args()
}
}

impl syn::parse::Parse for Column {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut result = Self {
name: None,
ty: None,
};

// Loop through the list of comma separated arguments to fill in the result one by one.
//
// Allowed syntax:
//
// #[column("name")]
// #[column(type = "type")]
// #[column("name", type = "type")]
// #[column(type = "type", "name")]
loop {
let lookahead = input.lookahead1();

if lookahead.peek(syn::LitStr) {
if result.name.is_some() {
return Err(syn::Error::new(input.span(), "duplicate column name"));
}
result.name = Some(input.parse()?);
} else if lookahead.peek(syn::Token![type]) {
if result.ty.is_some() {
return Err(syn::Error::new(input.span(), "duplicate column type"));
}
let _type_token: syn::Token![type] = input.parse()?;
let _eq_token: syn::Token![=] = input.parse()?;
result.ty = Some(input.parse()?);
} else {
return Err(lookahead.error());
}

if input.is_empty() {
break;
}
let _comma_token: syn::Token![,] = input.parse()?;
}

Ok(result)
}
}

mod kw {
syn::custom_keyword!(varchar);
}

#[derive(Debug)]
pub enum ColumnType {
VarChar(u64),
Custom(syn::LitStr),
}

impl syn::parse::Parse for ColumnType {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(syn::LitStr) {
Ok(Self::Custom(input.parse()?))
} else if lookahead.peek(kw::varchar) {
let _kw: kw::varchar = input.parse()?;
let content;
parenthesized!(content in input);
let lit: syn::LitInt = content.parse()?;
Ok(Self::VarChar(lit.base10_parse()?))
} else {
Err(lookahead.error())
}
}
}

impl quote::ToTokens for ColumnType {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
match self {
Self::VarChar(size) => quote! { db::Type::VarChar(#size) },
Self::Custom(custom) => quote! { db::Type::Custom(#custom) },
}
.to_tokens(tokens);
}
}
21 changes: 12 additions & 9 deletions crates/toasty-codegen/src/schema/field.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{BelongsTo, ColumnType, ErrorSet, HasMany, HasOne, Name};
use super::{BelongsTo, Column, ErrorSet, HasMany, HasOne, Name};

use syn::spanned::Spanned;

Expand Down Expand Up @@ -34,8 +34,8 @@ pub(crate) struct FieldAttr {
/// True if the field is indexed
pub(crate) index: bool,

/// Optional database column type
pub(crate) db: Option<ColumnType>,
/// Optional database column name and / or type
pub(crate) column: Option<Column>,
}

#[derive(Debug)]
Expand Down Expand Up @@ -66,7 +66,7 @@ impl Field {
unique: false,
auto: false,
index: false,
db: None,
column: None,
};

let mut ty = None;
Expand Down Expand Up @@ -136,18 +136,21 @@ impl Field {
} else {
ty = Some(FieldTy::HasOne(HasOne::from_ast(&field.ty, field.span())?));
}
} else if attr.path().is_ident("db") {
if attrs.db.is_some() {
errs.push(syn::Error::new_spanned(attr, "duplicate #[db] attribute"));
} else if attr.path().is_ident("column") {
if attrs.column.is_some() {
errs.push(syn::Error::new_spanned(
attr,
"duplicate #[column] attribute",
));
} else {
attrs.db = Some(ColumnType::from_ast(attr)?);
attrs.column = Some(Column::from_ast(attr)?);
}
} else if attr.path().is_ident("toasty") {
// todo
}
}

if ty.is_some() && attrs.db.is_some() {
if ty.is_some() && attrs.column.is_some() {
errs.push(syn::Error::new_spanned(
field,
"relation fields cannot have a database type",
Expand Down
33 changes: 0 additions & 33 deletions crates/toasty-codegen/src/schema/ty.rs

This file was deleted.

1 change: 1 addition & 0 deletions crates/toasty-core/src/driver/capability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ impl Capability {
// These types shouldn't be used as default string types, but handle them gracefully
None
}
db::Type::Custom(_) => None, // Custom types are not known to have a limited length
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/toasty-core/src/schema/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ mod constraint;
pub use constraint::{Constraint, ConstraintLength};

mod field;
pub use field::{Field, FieldId, FieldPrimitive, FieldTy};
pub use field::{Field, FieldId, FieldName, FieldPrimitive, FieldTy};

mod fk;
pub use fk::{ForeignKey, ForeignKeyField};
Expand Down
18 changes: 15 additions & 3 deletions crates/toasty-core/src/schema/app/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub struct Field {
pub id: FieldId,

/// The field name
pub name: String,
pub name: FieldName,

/// Primitive, relation, composite, ...
pub ty: FieldTy,
Expand All @@ -35,6 +35,18 @@ pub struct FieldId {
pub index: usize,
}

#[derive(Debug, Clone)]
pub struct FieldName {
pub app_name: String,
pub storage_name: Option<String>,
}

impl FieldName {
pub fn storage_name(&self) -> &str {
self.storage_name.as_ref().unwrap_or(&self.app_name)
}
}

#[derive(Clone)]
pub enum FieldTy {
Primitive(FieldPrimitive),
Expand All @@ -50,7 +62,7 @@ impl Field {
}

/// Gets the name.
pub fn name(&self) -> &str {
pub fn name(&self) -> &FieldName {
&self.name
}

Expand Down Expand Up @@ -81,7 +93,7 @@ impl Field {
/// Returns a fully qualified name for the field.
pub fn full_name(&self, schema: &Schema) -> String {
let model = schema.model(self.id.model);
format!("{}::{}", model.name.upper_camel_case(), self.name)
format!("{}::{}", model.name.upper_camel_case(), self.name.app_name)
}

/// If the field is a relation, return the relation's target ModelId.
Expand Down
6 changes: 4 additions & 2 deletions crates/toasty-core/src/schema/app/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,13 @@ impl Model {
}

pub fn field_by_name(&self, name: &str) -> Option<&Field> {
self.fields.iter().find(|field| field.name == name)
self.fields.iter().find(|field| field.name.app_name == name)
}

pub fn field_by_name_mut(&mut self, name: &str) -> Option<&mut Field> {
self.fields.iter_mut().find(|field| field.name == name)
self.fields
.iter_mut()
.find(|field| field.name.app_name == name)
}

pub fn find_by_id(&self, mut input: impl stmt::Input) -> stmt::Query {
Expand Down
2 changes: 1 addition & 1 deletion crates/toasty-core/src/schema/app/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ impl Builder {
panic!(
"no relation pair for {}::{}",
model.name.upper_camel_case(),
model.fields[index].name
model.fields[index].name.app_name
);
}
};
Expand Down
Loading
Loading