fix: make operation marker naming total

This commit is contained in:
2026-09-17 23:28:20 +09:00
parent 633f43530c
commit 019ab57e8f
3 changed files with 33 additions and 3 deletions
+16 -1
View File
@@ -473,7 +473,7 @@ fn normalize_operation(method: &mut TraitItemFn) -> syn::Result<Operation> {
}
Ok(Operation {
marker_ident: format_ident!("{}", to_pascal_case(&method.sig.ident)),
marker_ident: operation_marker_ident(&method.sig.ident),
method_ident: method.sig.ident.clone(),
operation_id,
method: http_method,
@@ -926,6 +926,21 @@ fn ident_text(ident: &Ident) -> String {
value.strip_prefix("r#").unwrap_or(&value).to_owned()
}
fn operation_marker_ident(method: &Ident) -> Ident {
let candidate = to_pascal_case(method);
if let Ok(mut marker) = syn::parse_str::<Ident>(&candidate) {
marker.set_span(method.span());
return marker;
}
let encoded = ident_text(method)
.as_bytes()
.iter()
.map(|byte| format!("{byte:02x}"))
.collect::<String>();
Ident::new(&format!("Operation{encoded}"), method.span())
}
fn to_pascal_case(ident: &Ident) -> String {
ident_text(ident)
.split('_')
+3 -2
View File
@@ -51,8 +51,9 @@
//! For `WidgetApi`, the macro emits `WidgetApiMetadata`, which implements [`ApiContract`],
//! plus a `widget_api_operations` module. That module contains one marker per Rust method
//! (`Create`, `Get`, ...), each implementing [`Operation`] and connecting metadata to concrete
//! body types at compile time. The `ApiContract::OPERATIONS` inventory is sorted by operation
//! ID, so its ordering is independent of source method order.
//! body types at compile time. Unusual method names which have no valid PascalCase form use the
//! deterministic `Operation<utf8-hex>` fallback. The `ApiContract::OPERATIONS` inventory is
//! sorted by operation ID, so its ordering is independent of source method order.
//!
//! Only empty and JSON bodies are accepted by this first contract. [`WireKind`] reserves
//! explicit variants for future transport work; accepting one requires a deliberate macro and
+14
View File
@@ -41,6 +41,15 @@ pub trait RawIdentifierApi {
async fn r#type(&self) -> Widget;
}
#[api]
pub trait UnusualIdentifierApi {
#[get("/underscores", operation_id = "unusual.underscores")]
async fn __(&self) -> Widget;
#[get("/underscore-digit", operation_id = "unusual.underscore_digit")]
async fn _0(&self) -> Widget;
}
fn assert_operation_types<O>()
where
O: Operation<
@@ -60,6 +69,11 @@ fn expansion_exposes_deterministic_metadata_and_type_connections() {
assert_named_wrapper::<wrapper_api_operations::Search>();
let raw = <raw_identifier_api_operations::Type as Operation>::METADATA;
assert_eq!(raw.operation_id, "raw.type");
let underscores = <unusual_identifier_api_operations::Operation5f5f as Operation>::METADATA;
assert_eq!(underscores.operation_id, "unusual.underscores");
let underscore_digit =
<unusual_identifier_api_operations::Operation5f30 as Operation>::METADATA;
assert_eq!(underscore_digit.operation_id, "unusual.underscore_digit");
let operations = <WidgetApiMetadata as ApiContract>::OPERATIONS;
assert_eq!(operations.len(), 3);