Skip to content
Open
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
58 changes: 58 additions & 0 deletions cot-cli/tests/migration_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,64 @@ fn create_models_foreign_key_two_migrations() {
assert_eq!(table_name, "cot__child");
}

#[test]
fn create_model_keywords() {
let generator = test_generator();
let src = include_str!("migration_generator/keywords.rs");
let source_files = vec![SourceFile::parse(PathBuf::from("main.rs"), src).unwrap()];

let migration = generator
.generate_migrations_as_generated_from_files(source_files)
.unwrap()
.unwrap();

assert_eq!(migration.migration_name, "m_0001_initial");
assert!(migration.dependencies.is_empty());

let (table_name, fields) = unwrap_create_model(&migration.operations[0]);
assert_eq!(table_name, "cot__const");
assert_eq!(fields.len(), 3);

let field = &fields[0];
assert_eq!(field.column_name, "id");

let field = &fields[1];
assert_eq!(field.column_name, "abstract");
assert_eq!(field.name.to_string(), "r#abstract");

let field = &fields[2];
assert_eq!(field.column_name, "type");
assert_eq!(field.name.to_string(), "r#type");
}

#[test]
fn create_model_keywords_source() {
let generator = test_generator();
let src = include_str!("migration_generator/keywords.rs");
let source_files = vec![SourceFile::parse(PathBuf::from("main.rs"), src).unwrap()];

let migration = generator
.generate_migrations_as_source_from_files(source_files)
.unwrap()
.unwrap();

assert!(
migration
.content
.contains(r#"::cot::db::Identifier::new("cot__const")"#)
);
assert!(
migration
.content
.contains(r#"::cot::db::Identifier::new("abstract")"#)
);
assert!(
migration
.content
.contains(r#"::cot::db::Identifier::new("type")"#)
);
}

/// Test that the migration generator can generate a "create model" migration
/// for a given model which compiles successfully.
#[test]
Expand Down
9 changes: 9 additions & 0 deletions cot-cli/tests/migration_generator/keywords.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use cot::db::{Auto, ForeignKey, model};

#[model]
struct r#const {
#[model(primary_key)]
id: Auto<i32>,
r#abstract: String,
r#type: i32,
}
59 changes: 55 additions & 4 deletions cot-codegen/src/model.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use darling::{FromDeriveInput, FromField, FromMeta};
use heck::ToSnakeCase;
use syn::ext::IdentExt;
use syn::spanned::Spanned;

use crate::symbol_resolver::SymbolResolver;
Expand Down Expand Up @@ -77,7 +78,7 @@ impl ModelOpts {
.map(as_field)
.collect::<Result<Vec<_>, _>>()?;

let mut original_name = self.ident.to_string();
let mut original_name = self.ident.unraw().to_string();
if args.model_type == ModelType::Migration {
original_name = original_name
.strip_prefix("_")
Expand All @@ -92,7 +93,7 @@ impl ModelOpts {
let table_name = if let Some(table_name) = &args.table_name {
table_name.clone()
} else {
original_name.clone().to_snake_case()
original_name.to_snake_case()
};

let primary_key_field = self.get_primary_key_field(&fields)?;
Expand Down Expand Up @@ -145,6 +146,7 @@ pub struct FieldOpts {
pub ty: syn::Type,
pub primary_key: darling::util::Flag,
pub unique: darling::util::Flag,
pub field_name: Option<String>,
}

impl FieldOpts {
Expand Down Expand Up @@ -204,8 +206,13 @@ impl FieldOpts {
symbol_resolver: &SymbolResolver,
self_reference: Option<&String>,
) -> Result<Field, syn::Error> {
let name = self.ident.as_ref().unwrap();
let column_name = name.to_string();
let name = self.ident.clone().expect("Only structs are supported");

let column_name = if let Some(specified_field_name) = &self.field_name {
specified_field_name.clone()
} else {
name.unraw().to_string()
};

let (auto_value, foreign_key) = (
self.find_type("cot::db::Auto", symbol_resolver).is_some(),
Expand Down Expand Up @@ -363,6 +370,23 @@ mod tests {
assert_eq!(model.field_count(), 2);
}

#[test]
fn model_opts_raw_name() {
let input: syn::DeriveInput = parse_quote! {
struct r#abstract {
#[model(primary_key)]
id: i32,
name: String,
}
};
let opts = ModelOpts::new_from_derive_input(&input).unwrap();
let model = opts
.as_model(&ModelArgs::default(), &SymbolResolver::new(vec![]))
.unwrap();
assert_eq!(model.name.to_string(), "r#abstract");
assert_eq!(model.table_name, "abstract");
}

#[test]
fn model_opts_as_model_migration() {
let input: syn::DeriveInput = parse_quote! {
Expand Down Expand Up @@ -458,6 +482,33 @@ mod tests {
assert!(field.unique);
}

#[test]
fn field_opts_raw_name() {
let input: syn::Field = parse_quote! {
r#abstract: String
};
let field_opts = FieldOpts::from_field(&input).unwrap();
let field = field_opts
.as_field(&SymbolResolver::new(vec![]), Some(&"TestModel".to_string()))
.unwrap();
assert_eq!(field.name.to_string(), "r#abstract");
assert_eq!(field.column_name, "abstract");
}

#[test]
fn field_opts_specified_field_name() {
let input: syn::Field = parse_quote! {
#[model(field_name="test_field")]
test: String
};
let field_opts = FieldOpts::from_field(&input).unwrap();
let field = field_opts
.as_field(&SymbolResolver::new(vec![]), Some(&"TestModel".to_string()))
.unwrap();
assert_eq!(field.name.to_string(), "test");
assert_eq!(field.column_name, "test_field");
}

#[test]
fn find_type_resolved() {
let input: syn::Type =
Expand Down
3 changes: 2 additions & 1 deletion cot-codegen/src/symbol_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::iter::FromIterator;
#[cfg(feature = "symbol-resolver")]
use std::path::Path;

use quote::format_ident;
#[cfg(feature = "symbol-resolver")]
use syn::UseTree;
#[cfg(feature = "symbol-resolver")]
Expand Down Expand Up @@ -88,7 +89,7 @@ impl SymbolResolver {
let mut new_segments: Vec<_> = symbol
.full_path_parts()
.map(|s| syn::PathSegment {
ident: syn::Ident::new(s, first_segment.ident.span()),
ident: format_ident!("{}", s, span = first_segment.ident.span()),
arguments: syn::PathArguments::None,
})
.collect();
Expand Down