use toasty::schema::{
app::FieldTy,
mapping::{self, FieldPrimitive, FieldStruct},
};
use toasty_core::stmt;
use uuid::Uuid;
use crate::prelude::*;
#[driver_test]
pub async fn basic_embedded_struct(test: &mut Test) {
#[derive(toasty::Embed)]
struct Address {
street: String,
city: String,
}
let db = test.setup_db(models!(Address)).await;
let schema = db.schema();
assert_struct!(schema.app.models, #{
Address::id(): toasty::schema::app::Model::EmbeddedStruct({
name.upper_camel_case(): "Address",
fields: [
{ name.app: Some("street") },
{ name.app: Some("city") },
],
}),
});
assert!(schema.db.tables.is_empty());
}
#[driver_test]
pub async fn root_model_with_embedded_field(test: &mut Test) {
#[derive(toasty::Embed)]
struct Address {
street: String,
city: String,
}
#[derive(toasty::Model)]
struct User {
#[key]
id: String,
#[allow(dead_code)]
address: Address,
}
let db = test.setup_db(models!(User, Address)).await;
let schema = db.schema();
assert_struct!(schema.app.models, #{
Address::id(): toasty::schema::app::Model::EmbeddedStruct({
name.upper_camel_case(): "Address",
fields: [
{ name.app: Some("street") },
{ name.app: Some("city") },
],
}),
User::id(): toasty::schema::app::Model::Root({
name.upper_camel_case(): "User",
fields: [
{ name.app: Some("id") },
{
name.app: Some("address"),
ty: FieldTy::Embedded({
target: == Address::id(),
}),
},
],
}),
});
assert_struct!(schema.db.tables, [
{
name: =~ r"users$",
columns: [
{ name: "id" },
{ name: "address_street" },
{ name: "address_city" },
],
},
]);
let user = &schema.app.models[&User::id()];
let user_table = schema.table_for(user);
let user_mapping = &schema.mapping.models[&User::id()];
assert_struct!(user_mapping, {
columns.len(): 3,
fields: [
mapping::Field::Primitive(FieldPrimitive {
column: == user_table.columns[0].id,
lowering: 0,
..
}),
mapping::Field::Struct(FieldStruct {
fields: [
mapping::Field::Primitive(FieldPrimitive {
column: == user_table.columns[1].id,
lowering: 1,
..
}),
mapping::Field::Primitive(FieldPrimitive {
column: == user_table.columns[2].id,
lowering: 2,
..
}),
],
..
}),
],
model_to_table.fields: [
_,
== stmt::Expr::project(
stmt::Expr::ref_self_field(user.as_root_unwrap().fields[1].id),
[0],
),
== stmt::Expr::project(
stmt::Expr::ref_self_field(user.as_root_unwrap().fields[1].id),
[1],
),
],
});
let table_to_model = user_mapping
.table_to_model
.lower_returning_model()
.into_record();
assert_struct!(
table_to_model.fields,
[
_,
stmt::Expr::Record(stmt::ExprRecord {
fields: [
== stmt::Expr::column(user_table.columns[1].id),
== stmt::Expr::column(user_table.columns[2].id),
],
}),
]
);
}
#[driver_test(id(ID))]
pub async fn create_and_query_embedded(t: &mut Test) -> Result<()> {
#[derive(Debug, toasty::Embed)]
struct Address {
street: String,
city: String,
}
#[derive(Debug, toasty::Model)]
struct User {
#[key]
#[auto]
id: ID,
name: String,
address: Address,
}
let mut db = t.setup_db(models!(User, Address)).await;
let mut user = User::create()
.name("Alice")
.address(Address {
street: "123 Main St".to_string(),
city: "Springfield".to_string(),
})
.exec(&mut db)
.await?;
let found = User::get_by_id(&mut db, &user.id).await?;
assert_eq!(found.address.street, "123 Main St");
assert_eq!(found.address.city, "Springfield");
user.update()
.address(Address {
street: "456 Oak Ave".to_string(),
city: "Shelbyville".to_string(),
})
.exec(&mut db)
.await?;
let found = User::get_by_id(&mut db, &user.id).await?;
assert_eq!(found.address.street, "456 Oak Ave");
User::filter_by_id(user.id)
.update()
.address(Address {
street: "789 Pine Rd".to_string(),
city: "Capital City".to_string(),
})
.exec(&mut db)
.await?;
let found = User::get_by_id(&mut db, &user.id).await?;
assert_eq!(found.address.street, "789 Pine Rd");
let id = user.id;
user.delete().exec(&mut db).await?;
assert_err!(User::get_by_id(&mut db, &id).await);
Ok(())
}
#[driver_test]
pub async fn embedded_struct_fields_codegen(test: &mut Test) {
#[derive(Debug, toasty::Embed)]
struct Address {
street: String,
city: String,
zip: String,
}
#[derive(Debug, toasty::Model)]
#[allow(dead_code)]
struct User {
#[key]
#[auto]
id: uuid::Uuid,
name: String,
address: Address,
}
let _db = test.setup_db(models!(User, Address)).await;
let _city_path = User::fields().address().city();
let address_fields = User::fields().address();
let _city_path_2 = address_fields.city();
let _address_city = Address::fields().city();
let _query = User::all().filter(User::fields().address().city().eq("Seattle"));
}
#[driver_test]
pub async fn query_embedded_struct_fields(t: &mut Test) -> Result<()> {
#[derive(Debug, toasty::Embed)]
struct Address {
street: String,
city: String,
zip: String,
}
#[derive(Debug, toasty::Model)]
#[key(partition = country, local = id)]
#[allow(dead_code)]
struct User {
#[auto]
id: uuid::Uuid,
country: String,
name: String,
address: Address,
}
let mut db = t.setup_db(models!(User, Address)).await;
let users_data = [
("USA", "Alice", "123 Main St", "Seattle", "98101"),
("USA", "Bob", "456 Oak Ave", "Seattle", "98102"),
("USA", "Charlie", "789 Pine Rd", "Portland", "97201"),
("USA", "Diana", "321 Elm St", "Portland", "97202"),
("CAN", "Eve", "111 Maple Dr", "Vancouver", "V6B 1A1"),
("CAN", "Frank", "222 Cedar Ln", "Vancouver", "V6B 2B2"),
("CAN", "Grace", "333 Birch Way", "Toronto", "M5H 1A1"),
];
for (country, name, street, city, zip) in users_data {
User::create()
.country(country)
.name(name)
.address(Address {
street: street.to_string(),
city: city.to_string(),
zip: zip.to_string(),
})
.exec(&mut db)
.await?;
}
let mut all_users = Vec::new();
for country in ["USA", "CAN"] {
let mut users = User::filter(User::fields().country().eq(country))
.exec(&mut db)
.await?;
all_users.append(&mut users);
}
assert_eq!(all_users.len(), 7);
let seattle_users = User::filter(
User::fields()
.country()
.eq("USA")
.and(User::fields().address().city().eq("Seattle")),
)
.exec(&mut db)
.await?;
assert_eq!(seattle_users.len(), 2);
let mut names: Vec<_> = seattle_users.iter().map(|u| u.name.as_str()).collect();
names.sort();
assert_eq!(names, ["Alice", "Bob"]);
let vancouver_users = User::filter(
User::fields()
.country()
.eq("CAN")
.and(User::fields().address().city().eq("Vancouver")),
)
.exec(&mut db)
.await?;
assert_eq!(vancouver_users.len(), 2);
let user_98101 = User::filter(
User::fields()
.country()
.eq("USA")
.and(User::fields().address().zip().eq("98101")),
)
.exec(&mut db)
.await?;
assert_eq!(user_98101.len(), 1);
assert_eq!(user_98101[0].name, "Alice");
Ok(())
}
#[driver_test(requires(sql))]
pub async fn query_embedded_fields_comparison_ops(t: &mut Test) -> Result<()> {
#[derive(Debug, toasty::Embed)]
struct Stats {
score: i64,
rank: i64,
}
#[derive(Debug, toasty::Model)]
#[allow(dead_code)]
struct Player {
#[key]
#[auto]
id: uuid::Uuid,
name: String,
stats: Stats,
}
let mut db = t.setup_db(models!(Player, Stats)).await;
for (name, score, rank) in [
("Alice", 100, 1),
("Bob", 85, 2),
("Charlie", 70, 3),
("Diana", 55, 4),
("Eve", 40, 5),
] {
Player::create()
.name(name)
.stats(Stats { score, rank })
.exec(&mut db)
.await?;
}
let high_scorers = Player::filter(Player::fields().stats().score().gt(80))
.exec(&mut db)
.await?;
assert_eq!(high_scorers.len(), 2);
let low_scorers = Player::filter(Player::fields().stats().score().le(55))
.exec(&mut db)
.await?;
assert_eq!(low_scorers.len(), 2);
let not_charlie = Player::filter(Player::fields().stats().score().ne(70))
.exec(&mut db)
.await?;
assert_eq!(not_charlie.len(), 4);
let mid_to_high = Player::filter(Player::fields().stats().score().ge(70))
.exec(&mut db)
.await?;
assert_eq!(mid_to_high.len(), 3);
Ok(())
}
#[driver_test(requires(sql))]
pub async fn query_embedded_multiple_fields(t: &mut Test) -> Result<()> {
#[derive(Debug, toasty::Embed)]
struct Coordinates {
x: i64,
y: i64,
z: i64,
}
#[derive(Debug, toasty::Model)]
#[allow(dead_code)]
struct Location {
#[key]
#[auto]
id: uuid::Uuid,
name: String,
coords: Coordinates,
}
let mut db = t.setup_db(models!(Location, Coordinates)).await;
for (name, x, y, z) in [
("Origin", 0, 0, 0),
("Point A", 10, 20, 0),
("Point B", 10, 30, 0),
("Point C", 10, 20, 5),
("Point D", 20, 20, 0),
] {
Location::create()
.name(name)
.coords(Coordinates { x, y, z })
.exec(&mut db)
.await?;
}
let matching = Location::filter(
Location::fields()
.coords()
.x()
.eq(10)
.and(Location::fields().coords().y().eq(20)),
)
.exec(&mut db)
.await?;
assert_eq!(matching.len(), 2);
let mut names: Vec<_> = matching.iter().map(|l| l.name.as_str()).collect();
names.sort();
assert_eq!(names, ["Point A", "Point C"]);
let exact_match = Location::filter(
Location::fields()
.coords()
.x()
.eq(10)
.and(Location::fields().coords().y().eq(20))
.and(Location::fields().coords().z().eq(0)),
)
.exec(&mut db)
.await?;
assert_eq!(exact_match.len(), 1);
assert_eq!(exact_match[0].name, "Point A");
Ok(())
}
#[driver_test(requires(sql))]
pub async fn update_with_embedded_field_filter(t: &mut Test) -> Result<()> {
#[derive(Debug, toasty::Embed)]
struct Metadata {
version: i64,
status: String,
}
#[derive(Debug, toasty::Model)]
#[allow(dead_code)]
struct Document {
#[key]
#[auto]
id: uuid::Uuid,
title: String,
meta: Metadata,
}
let mut db = t.setup_db(models!(Document, Metadata)).await;
for (title, version, status) in [
("Doc A", 1, "draft"),
("Doc B", 2, "draft"),
("Doc C", 1, "published"),
] {
Document::create()
.title(title)
.meta(Metadata {
version,
status: status.to_string(),
})
.exec(&mut db)
.await?;
}
Document::filter(
Document::fields()
.meta()
.status()
.eq("draft")
.and(Document::fields().meta().version().eq(1)),
)
.update()
.meta(Metadata {
version: 2,
status: "draft".to_string(),
})
.exec(&mut db)
.await?;
let doc_a = Document::filter(Document::fields().title().eq("Doc A"))
.exec(&mut db)
.await?;
assert_eq!(doc_a[0].meta.version, 2);
let doc_b = Document::filter(Document::fields().title().eq("Doc B"))
.exec(&mut db)
.await?;
assert_eq!(doc_b[0].meta.version, 2);
let doc_c = Document::filter(Document::fields().title().eq("Doc C"))
.exec(&mut db)
.await?;
assert_eq!(doc_c[0].meta.version, 1);
Ok(())
}
#[driver_test(id(ID))]
pub async fn partial_update_embedded_fields(t: &mut Test) -> Result<()> {
#[derive(Debug, toasty::Embed)]
struct Address {
street: String,
city: String,
zip: String,
}
#[derive(Debug, toasty::Model)]
struct User {
#[key]
#[auto]
id: ID,
name: String,
address: Address,
}
let mut db = t.setup_db(models!(User, Address)).await;
let mut user = User::create()
.name("Alice")
.address(Address {
street: "123 Main St".to_string(),
city: "Boston".to_string(),
zip: "02101".to_string(),
})
.exec(&mut db)
.await?;
assert_struct!(user.address, {
street: "123 Main St",
city: "Boston",
zip: "02101",
});
user.update()
.address(toasty::stmt::patch(Address::fields().city(), "Seattle"))
.exec(&mut db)
.await?;
assert_struct!(user.address, {
street: "123 Main St",
city: "Seattle",
zip: "02101",
});
let found = User::get_by_id(&mut db, &user.id).await?;
assert_struct!(found.address, {
street: "123 Main St",
city: "Seattle",
zip: "02101",
});
user.update()
.address(toasty::stmt::apply([
toasty::stmt::patch(Address::fields().city(), "Portland"),
toasty::stmt::patch(Address::fields().zip(), "97201"),
]))
.exec(&mut db)
.await?;
assert_struct!(user.address, {
street: "123 Main St",
city: "Portland",
zip: "97201",
});
let found = User::get_by_id(&mut db, &user.id).await?;
assert_struct!(found.address, {
street: "123 Main St",
city: "Portland",
zip: "97201",
});
user.update()
.address(toasty::stmt::patch(
Address::fields().street(),
"456 Oak Ave",
))
.address(toasty::stmt::patch(Address::fields().zip(), "97202"))
.exec(&mut db)
.await?;
assert_struct!(user.address, {
street: "456 Oak Ave",
city: "Portland",
zip: "97202",
});
let found = User::get_by_id(&mut db, &user.id).await?;
assert_struct!(found.address, {
street: "456 Oak Ave",
city: "Portland",
zip: "97202",
});
Ok(())
}
#[driver_test]
pub async fn deeply_nested_embedded_schema(test: &mut Test) {
#[derive(toasty::Embed)]
struct Location {
lat: i64,
lon: i64,
}
#[derive(toasty::Embed)]
struct City {
name: String,
location: Location,
}
#[derive(toasty::Embed)]
struct Address {
street: String,
city: City,
}
#[derive(toasty::Model)]
struct User {
#[key]
id: String,
#[allow(dead_code)]
address: Address,
}
let db = test.setup_db(models!(User, Address, City, Location)).await;
let schema = db.schema();
assert_struct!(schema.app.models, #{
Location::id(): toasty::schema::app::Model::EmbeddedStruct({
name.upper_camel_case(): "Location",
fields.len(): 2,
}),
City::id(): toasty::schema::app::Model::EmbeddedStruct({
name.upper_camel_case(): "City",
fields: [
{ name.app: Some("name") },
{
name.app: Some("location"),
ty: FieldTy::Embedded({
target: == Location::id(),
}),
},
],
}),
Address::id(): toasty::schema::app::Model::EmbeddedStruct({
name.upper_camel_case(): "Address",
fields: [
{ name.app: Some("street") },
{
name.app: Some("city"),
ty: FieldTy::Embedded({
target: == City::id(),
}),
},
],
}),
User::id(): toasty::schema::app::Model::Root({
name.upper_camel_case(): "User",
fields: [
{ name.app: Some("id") },
{
name.app: Some("address"),
ty: FieldTy::Embedded({
target: == Address::id(),
}),
},
],
}),
});
assert_struct!(schema.db.tables, [
{
name: =~ r"users$",
columns: [
{ name: "id" },
{ name: "address_street" },
{ name: "address_city_name" },
{ name: "address_city_location_lat" },
{ name: "address_city_location_lon" },
],
},
]);
let user = &schema.app.models[&User::id()];
let user_table = schema.table_for(user);
let user_mapping = &schema.mapping.models[&User::id()];
assert_eq!(
user_mapping.fields.len(),
2,
"User should have 2 fields: id and address"
);
let address_field = user_mapping.fields[1]
.as_struct()
.expect("User.address should be Field::Struct");
assert_eq!(
address_field.fields.len(),
2,
"Address should have 2 fields: street and city"
);
let street_field = address_field.fields[0]
.as_primitive()
.expect("Address.street should be Field::Primitive");
assert_eq!(
street_field.column, user_table.columns[1].id,
"street should map to address_street column"
);
let city_field = address_field.fields[1]
.as_struct()
.expect("Address.city should be Field::Struct");
assert_eq!(
city_field.fields.len(),
2,
"City should have 2 fields: name and location"
);
let city_name_field = city_field.fields[0]
.as_primitive()
.expect("City.name should be Field::Primitive");
assert_eq!(
city_name_field.column, user_table.columns[2].id,
"city.name should map to address_city_name column"
);
let location_field = city_field.fields[1]
.as_struct()
.expect("City.location should be Field::Struct");
assert_eq!(
location_field.fields.len(),
2,
"Location should have 2 fields: lat and lon"
);
let lat_field = location_field.fields[0]
.as_primitive()
.expect("Location.lat should be Field::Primitive");
assert_eq!(
lat_field.column, user_table.columns[3].id,
"location.lat should map to address_city_location_lat column"
);
let lon_field = location_field.fields[1]
.as_primitive()
.expect("Location.lon should be Field::Primitive");
assert_eq!(
lon_field.column, user_table.columns[4].id,
"location.lon should map to address_city_location_lon column"
);
assert_eq!(
address_field.columns.len(),
4,
"Address.columns should have 4 entries"
);
assert!(
address_field
.columns
.contains_key(&user_table.columns[1].id),
"Address.columns should contain address_street"
);
assert!(
address_field
.columns
.contains_key(&user_table.columns[2].id),
"Address.columns should contain address_city_name"
);
assert!(
address_field
.columns
.contains_key(&user_table.columns[3].id),
"Address.columns should contain address_city_location_lat"
);
assert!(
address_field
.columns
.contains_key(&user_table.columns[4].id),
"Address.columns should contain address_city_location_lon"
);
assert_eq!(
city_field.columns.len(),
3,
"City.columns should have 3 entries"
);
assert!(
city_field.columns.contains_key(&user_table.columns[2].id),
"City.columns should contain address_city_name"
);
assert!(
city_field.columns.contains_key(&user_table.columns[3].id),
"City.columns should contain address_city_location_lat"
);
assert!(
city_field.columns.contains_key(&user_table.columns[4].id),
"City.columns should contain address_city_location_lon"
);
assert_eq!(
location_field.columns.len(),
2,
"Location.columns should have 2 entries"
);
assert!(
location_field
.columns
.contains_key(&user_table.columns[3].id),
"Location.columns should contain address_city_location_lat"
);
assert!(
location_field
.columns
.contains_key(&user_table.columns[4].id),
"Location.columns should contain address_city_location_lon"
);
assert_eq!(
user_mapping.model_to_table.len(),
5,
"model_to_table should have 5 expressions"
);
assert_struct!(
user_mapping.model_to_table[1],
== stmt::Expr::project(
stmt::Expr::ref_self_field(user.as_root_unwrap().fields[1].id),
[0],
)
);
assert_struct!(
user_mapping.model_to_table[2],
== stmt::Expr::project(
stmt::Expr::ref_self_field(user.as_root_unwrap().fields[1].id),
[1, 0],
)
);
assert_struct!(
user_mapping.model_to_table[3],
== stmt::Expr::project(
stmt::Expr::ref_self_field(user.as_root_unwrap().fields[1].id),
[1, 1, 0],
)
);
assert_struct!(
user_mapping.model_to_table[4],
== stmt::Expr::project(
stmt::Expr::ref_self_field(user.as_root_unwrap().fields[1].id),
[1, 1, 1],
)
);
}
#[driver_test(id(ID))]
pub async fn crud_nested_embedded(t: &mut Test) -> Result<()> {
#[derive(Debug, toasty::Embed)]
struct Address {
street: String,
city: String,
}
#[derive(Debug, toasty::Embed)]
struct Office {
name: String,
address: Address,
}
#[derive(Debug, toasty::Model)]
struct Company {
#[key]
#[auto]
id: ID,
name: String,
headquarters: Office,
}
let mut db = t.setup_db(models!(Company, Office, Address)).await;
let mut company = Company::create()
.name("Acme")
.headquarters(Office {
name: "Main Office".to_string(),
address: Address {
street: "123 Main St".to_string(),
city: "Springfield".to_string(),
},
})
.exec(&mut db)
.await?;
assert_struct!(company.headquarters, {
name: "Main Office",
address: {
street: "123 Main St",
city: "Springfield",
},
});
let found = Company::get_by_id(&mut db, &company.id).await?;
assert_struct!(found.headquarters, {
name: "Main Office",
address: {
street: "123 Main St",
city: "Springfield",
},
});
company
.update()
.headquarters(Office {
name: "West Coast HQ".to_string(),
address: Address {
street: "456 Oak Ave".to_string(),
city: "Seattle".to_string(),
},
})
.exec(&mut db)
.await?;
let found = Company::get_by_id(&mut db, &company.id).await?;
assert_struct!(found.headquarters, {
name: "West Coast HQ",
address: {
street: "456 Oak Ave",
city: "Seattle",
},
});
Company::filter_by_id(company.id)
.update()
.headquarters(Office {
name: "East Coast HQ".to_string(),
address: Address {
street: "789 Pine Rd".to_string(),
city: "Boston".to_string(),
},
})
.exec(&mut db)
.await?;
let found = Company::get_by_id(&mut db, &company.id).await?;
assert_struct!(found.headquarters, {
name: "East Coast HQ",
address: {
street: "789 Pine Rd",
city: "Boston",
},
});
let id = company.id;
company.delete().exec(&mut db).await?;
assert_err!(Company::get_by_id(&mut db, &id).await);
Ok(())
}
#[driver_test(id(ID))]
pub async fn partial_update_nested_embedded(t: &mut Test) -> Result<()> {
#[derive(Debug, toasty::Embed)]
struct Address {
street: String,
city: String,
}
#[derive(Debug, toasty::Embed)]
struct Office {
name: String,
address: Address,
}
#[derive(Debug, toasty::Model)]
struct Company {
#[key]
#[auto]
id: ID,
name: String,
headquarters: Office,
}
let mut db = t.setup_db(models!(Company, Office, Address)).await;
let mut company = Company::create()
.name("Acme")
.headquarters(Office {
name: "Main Office".to_string(),
address: Address {
street: "123 Main St".to_string(),
city: "Boston".to_string(),
},
})
.exec(&mut db)
.await?;
company
.update()
.headquarters(toasty::stmt::patch(
Office::fields().address().city(),
"Seattle",
))
.exec(&mut db)
.await?;
let found = Company::get_by_id(&mut db, &company.id).await?;
assert_struct!(found.headquarters, {
name: "Main Office",
address: {
street: "123 Main St",
city: "Seattle",
},
});
company
.update()
.headquarters(toasty::stmt::patch(
Office::fields().name(),
"West Coast HQ",
))
.exec(&mut db)
.await?;
let found = Company::get_by_id(&mut db, &company.id).await?;
assert_struct!(found.headquarters, {
name: "West Coast HQ",
address: {
street: "123 Main St",
city: "Seattle",
},
});
company
.update()
.headquarters(toasty::stmt::apply([
toasty::stmt::patch(Office::fields().name(), "East Coast HQ"),
toasty::stmt::patch(Office::fields().address().city(), "Boston"),
]))
.exec(&mut db)
.await?;
let found = Company::get_by_id(&mut db, &company.id).await?;
assert_struct!(found.headquarters, {
name: "East Coast HQ",
address: {
street: "123 Main St",
city: "Boston",
},
});
Ok(())
}
#[driver_test(id(ID))]
pub async fn query_based_partial_update_embedded(t: &mut Test) -> Result<()> {
#[derive(Debug, toasty::Embed)]
struct Address {
street: String,
city: String,
zip: String,
}
#[derive(Debug, toasty::Model)]
struct User {
#[key]
#[auto]
id: ID,
name: String,
address: Address,
}
let mut db = t.setup_db(models!(User, Address)).await;
let user = User::create()
.name("Alice")
.address(Address {
street: "123 Main St".to_string(),
city: "Boston".to_string(),
zip: "02101".to_string(),
})
.exec(&mut db)
.await?;
User::filter_by_id(user.id)
.update()
.address(toasty::stmt::patch(Address::fields().city(), "Seattle"))
.exec(&mut db)
.await?;
let found = User::get_by_id(&mut db, &user.id).await?;
assert_struct!(found.address, {
street: "123 Main St",
city: "Seattle",
zip: "02101",
});
User::filter_by_id(user.id)
.update()
.address(toasty::stmt::apply([
toasty::stmt::patch(Address::fields().city(), "Portland"),
toasty::stmt::patch(Address::fields().zip(), "97201"),
]))
.exec(&mut db)
.await?;
let found = User::get_by_id(&mut db, &user.id).await?;
assert_struct!(found.address, {
street: "123 Main St",
city: "Portland",
zip: "97201",
});
Ok(())
}
#[driver_test(id(ID))]
pub async fn embedded_struct_with_jiff_fields(t: &mut Test) -> Result<()> {
#[derive(Debug, toasty::Embed)]
struct Schedule {
starts_at: jiff::Timestamp,
due_date: jiff::civil::Date,
reminder_time: jiff::civil::Time,
scheduled_at: jiff::civil::DateTime,
}
#[derive(Debug, toasty::Model)]
struct Event {
#[key]
#[auto]
id: ID,
name: String,
schedule: Schedule,
}
let mut db = t.setup_db(models!(Event, Schedule)).await;
let starts_at = jiff::Timestamp::from_second(1_700_000_000).unwrap();
let due_date = jiff::civil::date(2025, 6, 15);
let reminder_time = jiff::civil::time(9, 30, 0, 0);
let scheduled_at = jiff::civil::datetime(2025, 6, 15, 9, 30, 0, 0);
let event = Event::create()
.name("team sync")
.schedule(Schedule {
starts_at,
due_date,
reminder_time,
scheduled_at,
})
.exec(&mut db)
.await?;
let found = Event::get_by_id(&mut db, &event.id).await?;
assert_struct!(found.schedule, {
starts_at: == starts_at,
due_date: == due_date,
reminder_time: == reminder_time,
scheduled_at: == scheduled_at,
});
Ok(())
}
#[driver_test(id(ID))]
pub async fn unit_enum_in_embedded_struct(t: &mut Test) -> Result<()> {
#[derive(Debug, PartialEq, toasty::Embed)]
enum Priority {
#[column(variant = 1)]
Low,
#[column(variant = 2)]
Normal,
#[column(variant = 3)]
High,
}
#[derive(Debug, toasty::Embed)]
struct Meta {
label: String,
priority: Priority,
}
#[derive(Debug, toasty::Model)]
struct Task {
#[key]
#[auto]
id: ID,
meta: Meta,
}
let mut db = t.setup_db(models!(Task, Meta, Priority)).await;
let mut task = Task::create()
.meta(Meta {
label: "fix bug".to_string(),
priority: Priority::High,
})
.exec(&mut db)
.await?;
let found = Task::get_by_id(&mut db, &task.id).await?;
assert_eq!(found.meta.label, "fix bug");
assert_eq!(found.meta.priority, Priority::High);
task.update()
.meta(toasty::stmt::patch(
Meta::fields().priority().into(),
Priority::Normal,
))
.exec(&mut db)
.await?;
let found = Task::get_by_id(&mut db, &task.id).await?;
assert_eq!(found.meta.priority, Priority::Normal);
Ok(())
}
#[driver_test(id(ID))]
pub async fn embedded_struct_with_uuid_field(t: &mut Test) -> Result<()> {
#[derive(Debug, toasty::Embed)]
struct Meta {
ref_id: Uuid,
label: String,
}
#[derive(Debug, toasty::Model)]
struct Item {
#[key]
#[auto]
id: ID,
name: String,
meta: Meta,
}
let mut db = t.setup_db(models!(Item, Meta)).await;
let ref_id = Uuid::new_v4();
let item = Item::create()
.name("widget")
.meta(Meta {
ref_id,
label: "v1".to_string(),
})
.exec(&mut db)
.await?;
let found = Item::get_by_id(&mut db, &item.id).await?;
assert_eq!(found.meta.ref_id, ref_id);
assert_eq!(found.meta.label, "v1");
Ok(())
}