#[derive(Debug, PartialEq, Eq)]
pub struct MappingPaths {
pub equivalent: syn::Path,
pub runtime: syn::Path,
pub integration: Option<crate::integrations::Active>,
}
pub fn generate_paths<C: crate::conventions::Conventions>(
ident: &syn::Ident,
path: syn::Path,
integrations: &crate::parse_top_level_meta::filters::Integrations,
runtime_crate: Option<syn::Path>,
) -> syn::Result<MappingPaths> {
crate::attributes::reject_self_link(&path, ident, "validate_non_nullable", "mirrors")?;
let what = "domain merge patch model";
C::check_merge_patch_name(crate::names::path_name(&path, what)?, &path, what)?;
let registry = C::integrations();
let supported = registry
.iter()
.filter(|integration| integration.supports_validate_non_nullable())
.map(|integration| integration.name())
.collect::<Vec<_>>()
.join(", ");
let mut errors = crate::errors::Errors::default();
let mut integration: Option<crate::integrations::Active> = None;
for active in crate::mapping::filters::resolve(®istry, integrations) {
let flag = &active.flag;
if !active.integration.supports_validate_non_nullable() {
errors.push(syn::Error::new(
flag.span(),
crate::errors::message(
&format!("`{flag}` does not apply to ValidateNonNullable"),
&format!("Name the validation library defining validate_non_nullable: one of {supported}"),
),
));
} else if let Some(first) = &integration {
errors.push(syn::Error::new(
flag.span(),
crate::errors::message(
&format!(
"`{}` and `{flag}` would both define validate_non_nullable",
first.flag
),
"Keep the one validation library the DTO uses",
),
));
} else {
integration = Some(active);
}
}
errors.finish(MappingPaths {
equivalent: path,
runtime: runtime_crate.unwrap_or_else(C::core_crate),
integration,
})
}
#[cfg(test)]
mod tests {
#[test]
fn test_generating_mapping_paths_succeeds_when_all_preconditions_are_met() {
let mapping_paths = super::generate_paths::<crate::conventions::DefaultConventions>(
"e::format_ident!("MergePatchOrderDto"),
syn::parse_str("domain::models::merge_patch_order::MergePatchOrderModel").unwrap(),
&crate::parse_top_level_meta::filters::Integrations::default(),
None,
)
.unwrap();
pretty_assertions::assert_eq!(
mapping_paths,
super::MappingPaths {
equivalent: syn::parse_str(
"domain::models::merge_patch_order::MergePatchOrderModel"
)
.unwrap(),
runtime: syn::parse_quote!(::queryweave),
integration: None,
}
);
}
#[test]
fn test_generating_mapping_paths_fails_for_a_name_that_is_not_a_merge_patch_one() {
for path in ["merge_patch_order_model", "OrderModel"] {
assert!(
super::generate_paths::<crate::conventions::DefaultConventions>(
"e::format_ident!("MergePatchOrderDto"),
syn::parse_str(path).unwrap(),
&crate::parse_top_level_meta::filters::Integrations::default(),
None
)
.is_err()
);
}
}
#[test]
fn test_generating_mapping_paths_rejects_a_struct_mirroring_itself() {
let generate = |path: &str| {
super::generate_paths::<crate::conventions::DefaultConventions>(
"e::format_ident!("MergePatchOrderDto"),
syn::parse_str(path).unwrap(),
&crate::parse_top_level_meta::filters::Integrations::default(),
None,
)
};
assert!(generate("Self").is_err());
assert!(generate("MergePatchOrderDto").is_err());
assert!(generate("self::MergePatchOrderDto").is_err());
assert!(generate("api::MergePatchOrderDto").is_ok());
}
fn with_flags(names: &[&str]) -> syn::Result<super::MappingPaths> {
super::generate_paths::<crate::conventions::DefaultConventions>(
"e::format_ident!("MergePatchOrderDto"),
syn::parse_quote!(MergePatchOrderModel),
&crate::parse_top_level_meta::filters::Integrations::of(names),
None,
)
}
#[test]
fn test_one_validation_integration_is_kept() {
assert!(with_flags(&[]).unwrap().integration.is_none());
let integration = with_flags(&["garde"]).unwrap().integration.unwrap();
assert_eq!(integration.flag, "garde");
}
#[test]
fn test_other_or_several_integrations_are_rejected() {
assert!(with_flags(&["serde"]).is_err());
assert!(with_flags(&["validator", "garde"]).is_err());
}
}