diff --git a/crates/entity-derive-impl/src/entity/view.rs b/crates/entity-derive-impl/src/entity/view.rs index 26349d4..b7d7ad1 100644 --- a/crates/entity-derive-impl/src/entity/view.rs +++ b/crates/entity-derive-impl/src/entity/view.rs @@ -6,7 +6,8 @@ //! For an entity with at least one `#[join(...)]`, generates: //! //! - `{Entity}View` — a flat struct with every entity column plus the declared -//! joined columns, deriving `sqlx::FromRow` and `serde::Serialize` +//! joined columns, deriving `sqlx::FromRow` and `serde::Serialize` (plus +//! `utoipa::ToSchema` when the `api` feature is enabled) //! - `{Entity}View::SELECT` — the canonical `SELECT ... FROM ... JOIN ...` //! fragment (no WHERE), for custom filters via `format!("{} WHERE ...", //! TicketView::SELECT)` @@ -68,6 +69,11 @@ pub fn generate(entity: &EntityDef) -> TokenStream { let doc = format!( "Joined read model for [`{entity_name}`], generated from its `#[join(...)]` declarations." ); + let api_derive = if cfg!(feature = "api") { + quote! { #[derive(utoipa::ToSchema)] } + } else { + TokenStream::new() + }; let select_doc = format!( "Canonical `SELECT ... FROM ... JOIN ...` fragment (no WHERE clause).\n\n\ Compose custom filters with\n\ @@ -78,6 +84,7 @@ pub fn generate(entity: &EntityDef) -> TokenStream { #marker #[doc = #doc] #[derive(Debug, Clone, serde::Serialize, sqlx::FromRow)] + #api_derive #vis struct #view_name { #(#entity_fields,)* #(#join_fields,)* @@ -182,6 +189,12 @@ mod tests { assert!(code.contains("pub async fn list")); } + #[test] + fn view_struct_derives_to_schema_only_with_api_feature() { + let code = generate(&ticket_entity()).to_string(); + assert_eq!(code.contains("utoipa :: ToSchema"), cfg!(feature = "api")); + } + #[test] fn select_qualifies_and_aliases_columns() { let sql = build_select(&ticket_entity()); diff --git a/crates/entity-derive/tests/cases/pass/join_view.rs b/crates/entity-derive/tests/cases/pass/join_view.rs new file mode 100644 index 0000000..823abe1 --- /dev/null +++ b/crates/entity-derive/tests/cases/pass/join_view.rs @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2025-2026 RAprogramm +// SPDX-License-Identifier: MIT + +use entity_derive::Entity; +use uuid::Uuid; + +#[derive(Debug, Clone, Entity, serde::Serialize, serde::Deserialize)] +#[join(airports as origin, on = origin_iata = iata, fields( + lat as origin_lat: f64, + city as origin_city: String +))] +#[join(airports as dest, on = destination_iata = iata, fields( + lat as destination_lat: f64 +))] +#[entity(table = "tickets")] +pub struct Ticket { + #[id] + pub id: Uuid, + + #[field(create, response)] + pub origin_iata: String, + + #[field(create, response)] + pub destination_iata: String, +} + +fn assert_schema() {} + +fn main() { + let _select: &str = TicketView::SELECT; + assert_schema::(); +}