fix(interface): harden generated public API
This commit is contained in:
@@ -1,4 +1,10 @@
|
||||
//! Procedural macros backing the public `wip-interface` authoring facade.
|
||||
//!
|
||||
//! This is an implementation crate. Applications should depend on
|
||||
//! `wip-interface`, which re-exports both macros alongside the traits and types
|
||||
//! used by their generated code.
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
@@ -470,13 +476,18 @@ fn parse_operation_result(output: &ReturnType) -> syn::Result<Type> {
|
||||
_ => None,
|
||||
});
|
||||
let result = types.next().cloned();
|
||||
if arguments.args.len() != 1 || result.is_none() || types.next().is_some() {
|
||||
if arguments.args.len() != 1 || types.next().is_some() {
|
||||
return Err(Error::new_spanned(
|
||||
arguments,
|
||||
"`OperationResult` must have exactly one application result type",
|
||||
));
|
||||
}
|
||||
let result = result.expect("checked above");
|
||||
let result = result.ok_or_else(|| {
|
||||
Error::new_spanned(
|
||||
arguments,
|
||||
"`OperationResult` must have exactly one application result type",
|
||||
)
|
||||
})?;
|
||||
if direct_option(&result)?.is_some() || contains_option(&result) {
|
||||
return Err(Error::new_spanned(
|
||||
result,
|
||||
@@ -490,8 +501,13 @@ fn argument_item(operation: &Operation) -> TokenStream2 {
|
||||
let arguments_ident = &operation.arguments_ident;
|
||||
let fields = operation.parameters.iter().map(|parameter| {
|
||||
let ident = ¶meter.ident;
|
||||
let name = ¶meter.name;
|
||||
let ty = parameter_rust_type(parameter);
|
||||
quote!(pub #ident: #ty)
|
||||
let documentation = format!("Decoded `{name}` operation argument.");
|
||||
quote!(
|
||||
#[doc = #documentation]
|
||||
pub #ident: #ty
|
||||
)
|
||||
});
|
||||
let decoders = operation.parameters.iter().map(|parameter| {
|
||||
let ident = ¶meter.ident;
|
||||
@@ -698,7 +714,12 @@ fn derive_record(
|
||||
let mut parsed_fields = Vec::new();
|
||||
let mut names = HashSet::new();
|
||||
for field in fields {
|
||||
let field_ident = field.ident.clone().expect("named fields have identifiers");
|
||||
let Some(field_ident) = field.ident.clone() else {
|
||||
return Err(Error::new_spanned(
|
||||
field,
|
||||
"`WipType` record fields must be named",
|
||||
));
|
||||
};
|
||||
let name = source_name(&field_ident);
|
||||
if !names.insert(name.clone()) {
|
||||
return Err(Error::new(
|
||||
@@ -984,12 +1005,17 @@ fn derive_union_enum(
|
||||
let mut variants = Vec::new();
|
||||
for variant in &data.variants {
|
||||
let Fields::Unnamed(fields) = &variant.fields else {
|
||||
unreachable!("enum shape was checked before union generation");
|
||||
return Err(Error::new_spanned(
|
||||
variant,
|
||||
"a `WipType` union variant must contain exactly one unnamed field",
|
||||
));
|
||||
};
|
||||
let Some(field) = fields.unnamed.first() else {
|
||||
return Err(Error::new_spanned(
|
||||
fields,
|
||||
"a `WipType` union variant must contain exactly one unnamed field",
|
||||
));
|
||||
};
|
||||
let field = fields
|
||||
.unnamed
|
||||
.first()
|
||||
.expect("union variants have one field");
|
||||
if direct_option(&field.ty)?.is_some() || contains_option(&field.ty) {
|
||||
return Err(Error::new_spanned(
|
||||
&field.ty,
|
||||
|
||||
Reference in New Issue
Block a user