Skip to main content

krabka_protocol_codegen/emit/
protocol_request.rs

1//! Emit `impl crate::ProtocolRequest for <RequestType>` blocks.
2//!
3//! This module emits blocks only for `MessageType::Request` schemas. It
4//! derives the matching Response type from the type name: it replaces the
5//! `Request` suffix with `Response`, then converts the result to a
6//! `snake_case` module name.
7
8use quote::{format_ident, quote};
9
10use crate::{
11    emit::{EmitError, common::validate},
12    ir::{MessageSpec, MessageType},
13    name_conv,
14};
15
16/// Emit the `impl crate::ProtocolRequest for <Type>` block for a request spec.
17///
18/// Returns `None` if the spec is not a `MessageType::Request`.
19///
20/// # Errors
21/// Returns an error when the emitted impl is not valid Rust, which is an
22/// emitter bug.
23pub fn emit_protocol_request(spec: &MessageSpec) -> Result<Option<String>, EmitError> {
24    if spec.message_type != MessageType::Request {
25        return Ok(None);
26    }
27
28    let type_name = name_conv::type_name(&spec.name);
29
30    // Derive the response type name: replace trailing "Request" with "Response".
31    // Every Kafka request has a matching response with exactly this naming convention.
32    // Fallback: append "Response" directly (should not occur in practice).
33    let response_type_name = type_name.strip_suffix("Request").map_or_else(
34        || format!("{type_name}Response"),
35        |prefix| format!("{prefix}Response"),
36    );
37
38    let response_module = name_conv::module_name(&response_type_name);
39
40    let type_ident = format_ident!("{type_name}");
41    let response_module_ident = format_ident!("{response_module}");
42    let response_type_ident = format_ident!("{response_type_name}");
43
44    let tokens = quote! {
45        impl crate::ProtocolRequest for #type_ident {
46            const API_KEY: i16 = API_KEY;
47            const MIN_VERSION: i16 = MIN_VERSION;
48            const MAX_VERSION: i16 = MAX_VERSION;
49            const LATEST_STABLE_VERSION: i16 = LATEST_STABLE_VERSION;
50            const FLEXIBLE_MIN: i16 = FLEXIBLE_MIN;
51            type Response = super::#response_module_ident::#response_type_ident;
52        }
53    };
54
55    // Validate at generation time; rustfmt (applied by the caller) does the
56    // actual formatting. See `crate::fmt`.
57    validate::<syn::ItemImpl>(&tokens, "ProtocolRequest impl")?;
58
59    Ok(Some(format!("\n{tokens}")))
60}