use proc_macro2::TokenStream;
use quote::quote;
use crate::entity::parse::EntityDef;
pub fn generate_info_code(entity: &EntityDef) -> TokenStream {
let api_config = entity.api_config();
let title_code = if let Some(ref title) = api_config.title {
quote! { openapi.info.title = #title.to_string(); }
} else {
TokenStream::new()
};
let description_code = if let Some(ref description) = api_config.description {
quote! { openapi.info.description = Some(#description.to_string()); }
} else if let Some(doc) = entity.doc() {
quote! { openapi.info.description = Some(#doc.to_string()); }
} else {
TokenStream::new()
};
let version_code = if let Some(ref version) = api_config.api_version {
quote! { openapi.info.version = #version.to_string(); }
} else {
TokenStream::new()
};
let license_code = match (&api_config.license, &api_config.license_url) {
(Some(name), Some(url)) => {
quote! {
openapi.info.license = Some(
info::LicenseBuilder::new()
.name(#name)
.url(Some(#url))
.build()
);
}
}
(Some(name), None) => {
quote! {
openapi.info.license = Some(
info::LicenseBuilder::new()
.name(#name)
.build()
);
}
}
_ => TokenStream::new()
};
let has_contact = api_config.contact_name.is_some()
|| api_config.contact_email.is_some()
|| api_config.contact_url.is_some();
let contact_code = if has_contact {
let name = api_config.contact_name.as_deref().unwrap_or("");
let email = api_config.contact_email.as_deref();
let url = api_config.contact_url.as_deref();
let email_setter = if let Some(e) = email {
quote! { .email(Some(#e)) }
} else {
TokenStream::new()
};
let url_setter = if let Some(u) = url {
quote! { .url(Some(#u)) }
} else {
TokenStream::new()
};
quote! {
openapi.info.contact = Some(
info::ContactBuilder::new()
.name(Some(#name))
#email_setter
#url_setter
.build()
);
}
} else {
TokenStream::new()
};
let deprecated_code = if api_config.is_deprecated() {
let version = api_config.deprecated_in.as_deref().unwrap_or("unknown");
let msg = format!("Deprecated since {}", version);
quote! {
if let Some(ref desc) = openapi.info.description {
openapi.info.description = Some(format!("**DEPRECATED**: {}\n\n{}", #msg, desc));
} else {
openapi.info.description = Some(format!("**DEPRECATED**: {}", #msg));
}
}
} else {
TokenStream::new()
};
quote! {
#title_code
#description_code
#version_code
#license_code
#contact_code
#deprecated_code
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn generate_info_empty_config() {
let input: syn::DeriveInput = syn::parse_quote! {
#[entity(table = "users", api(tag = "Users", handlers))]
pub struct User {
#[id]
pub id: uuid::Uuid,
}
};
let entity = EntityDef::from_derive_input(&input).unwrap();
let output = generate_info_code(&entity);
assert!(output.is_empty() || output.to_string().is_empty());
}
#[test]
fn generate_info_with_title() {
let input: syn::DeriveInput = syn::parse_quote! {
#[entity(table = "users", api(tag = "Users", title = "User API", handlers))]
pub struct User {
#[id]
pub id: uuid::Uuid,
}
};
let entity = EntityDef::from_derive_input(&input).unwrap();
let output = generate_info_code(&entity);
let output_str = output.to_string();
assert!(output_str.contains("openapi . info . title"));
assert!(output_str.contains("User API"));
}
#[test]
fn generate_info_with_description() {
let input: syn::DeriveInput = syn::parse_quote! {
#[entity(table = "users", api(tag = "Users", description = "Manage users", handlers))]
pub struct User {
#[id]
pub id: uuid::Uuid,
}
};
let entity = EntityDef::from_derive_input(&input).unwrap();
let output = generate_info_code(&entity);
let output_str = output.to_string();
assert!(output_str.contains("openapi . info . description"));
assert!(output_str.contains("Manage users"));
}
#[test]
fn generate_info_with_version() {
let input: syn::DeriveInput = syn::parse_quote! {
#[entity(table = "users", api(tag = "Users", api_version = "2.0.0", handlers))]
pub struct User {
#[id]
pub id: uuid::Uuid,
}
};
let entity = EntityDef::from_derive_input(&input).unwrap();
let output = generate_info_code(&entity);
let output_str = output.to_string();
assert!(output_str.contains("openapi . info . version"));
assert!(output_str.contains("2.0.0"));
}
#[test]
fn generate_info_with_license() {
let input: syn::DeriveInput = syn::parse_quote! {
#[entity(table = "users", api(tag = "Users", license = "MIT", handlers))]
pub struct User {
#[id]
pub id: uuid::Uuid,
}
};
let entity = EntityDef::from_derive_input(&input).unwrap();
let output = generate_info_code(&entity);
let output_str = output.to_string();
assert!(output_str.contains("openapi . info . license"));
assert!(output_str.contains("MIT"));
}
#[test]
fn generate_info_with_license_and_url() {
let input: syn::DeriveInput = syn::parse_quote! {
#[entity(table = "users", api(
tag = "Users",
license = "MIT",
license_url = "https://opensource.org/licenses/MIT",
handlers
))]
pub struct User {
#[id]
pub id: uuid::Uuid,
}
};
let entity = EntityDef::from_derive_input(&input).unwrap();
let output = generate_info_code(&entity);
let output_str = output.to_string();
assert!(output_str.contains("LicenseBuilder"));
assert!(output_str.contains("MIT"));
assert!(output_str.contains("opensource.org"));
}
#[test]
fn generate_info_with_contact_name() {
let input: syn::DeriveInput = syn::parse_quote! {
#[entity(table = "users", api(tag = "Users", contact_name = "API Team", handlers))]
pub struct User {
#[id]
pub id: uuid::Uuid,
}
};
let entity = EntityDef::from_derive_input(&input).unwrap();
let output = generate_info_code(&entity);
let output_str = output.to_string();
assert!(output_str.contains("openapi . info . contact"));
assert!(output_str.contains("ContactBuilder"));
assert!(output_str.contains("API Team"));
}
#[test]
fn generate_info_with_contact_email() {
let input: syn::DeriveInput = syn::parse_quote! {
#[entity(table = "users", api(
tag = "Users",
contact_name = "Support",
contact_email = "support@example.com",
handlers
))]
pub struct User {
#[id]
pub id: uuid::Uuid,
}
};
let entity = EntityDef::from_derive_input(&input).unwrap();
let output = generate_info_code(&entity);
let output_str = output.to_string();
assert!(output_str.contains("email"));
assert!(output_str.contains("support@example.com"));
}
#[test]
fn generate_info_with_contact_url() {
let input: syn::DeriveInput = syn::parse_quote! {
#[entity(table = "users", api(
tag = "Users",
contact_name = "Support",
contact_url = "https://example.com/support",
handlers
))]
pub struct User {
#[id]
pub id: uuid::Uuid,
}
};
let entity = EntityDef::from_derive_input(&input).unwrap();
let output = generate_info_code(&entity);
let output_str = output.to_string();
assert!(output_str.contains("url"));
assert!(output_str.contains("example.com/support"));
}
#[test]
fn generate_info_with_full_contact() {
let input: syn::DeriveInput = syn::parse_quote! {
#[entity(table = "users", api(
tag = "Users",
contact_name = "API Team",
contact_email = "api@example.com",
contact_url = "https://example.com",
handlers
))]
pub struct User {
#[id]
pub id: uuid::Uuid,
}
};
let entity = EntityDef::from_derive_input(&input).unwrap();
let output = generate_info_code(&entity);
let output_str = output.to_string();
assert!(output_str.contains("ContactBuilder"));
assert!(output_str.contains("API Team"));
assert!(output_str.contains("api@example.com"));
assert!(output_str.contains("example.com"));
}
#[test]
fn generate_info_deprecated() {
let input: syn::DeriveInput = syn::parse_quote! {
#[entity(table = "users", api(tag = "Users", deprecated_in = "2.0", handlers))]
pub struct User {
#[id]
pub id: uuid::Uuid,
}
};
let entity = EntityDef::from_derive_input(&input).unwrap();
let output = generate_info_code(&entity);
let output_str = output.to_string();
assert!(output_str.contains("DEPRECATED"));
assert!(output_str.contains("2.0"));
}
#[test]
fn generate_info_full_config() {
let input: syn::DeriveInput = syn::parse_quote! {
#[entity(table = "users", api(
tag = "Users",
title = "User API",
description = "User management endpoints",
api_version = "1.0.0",
license = "MIT",
license_url = "https://opensource.org/licenses/MIT",
contact_name = "Dev Team",
contact_email = "dev@example.com",
contact_url = "https://example.com",
handlers
))]
pub struct User {
#[id]
pub id: uuid::Uuid,
}
};
let entity = EntityDef::from_derive_input(&input).unwrap();
let output = generate_info_code(&entity);
let output_str = output.to_string();
assert!(output_str.contains("User API"));
assert!(output_str.contains("User management endpoints"));
assert!(output_str.contains("1.0.0"));
assert!(output_str.contains("MIT"));
assert!(output_str.contains("Dev Team"));
}
#[test]
fn generate_info_uses_entity_doc_as_description() {
let input: syn::DeriveInput = syn::parse_quote! {
#[entity(table = "users", api(tag = "Users", handlers))]
pub struct User {
#[id]
pub id: uuid::Uuid,
}
};
let entity = EntityDef::from_derive_input(&input).unwrap();
let output = generate_info_code(&entity);
let output_str = output.to_string();
assert!(output_str.contains("openapi . info . description"));
assert!(output_str.contains("User entity"));
}
}