Skip to main content

krabka_protocol_codegen/
type_map.rs

1//! Map a schema field type string to a Rust type expression.
2
3/// Owned-flavor Rust type for a schema type.
4///
5/// `nullable` and `is_struct_ref` shape the wrapping. The caller must resolve struct references and pass them
6/// in as `is_struct_ref = true`, together with the resolved Rust path, for
7/// example `"super::common::ProduceTopic"`.
8#[must_use]
9pub fn owned_type(schema_type: &str, nullable: bool, struct_path: Option<&str>) -> String {
10    let inner = inner_owned(schema_type, struct_path);
11    if nullable {
12        format!("Option<{inner}>")
13    } else {
14        inner
15    }
16}
17
18/// Borrowed-flavor Rust type. Strings and bytes become `&'a str` and
19/// `&'a [u8]`, arrays own their outer `Vec`, and struct references take the
20/// `<'a>` form.
21#[must_use]
22pub fn borrowed_type(schema_type: &str, nullable: bool, struct_path: Option<&str>) -> String {
23    let inner = inner_borrowed(schema_type, struct_path);
24    if nullable {
25        format!("Option<{inner}>")
26    } else {
27        inner
28    }
29}
30
31fn inner_owned(t: &str, struct_path: Option<&str>) -> String {
32    if let Some(elem) = t.strip_prefix("[]") {
33        let elem_path = struct_path; // struct_path applies to the element
34        return format!("Vec<{}>", inner_owned(elem, elem_path));
35    }
36    match t {
37        "bool" => "bool".into(),
38        "int8" => "i8".into(),
39        "int16" => "i16".into(),
40        "int32" => "i32".into(),
41        "int64" => "i64".into(),
42        "uint16" => "u16".into(),
43        "uint32" => "u32".into(),
44        "float64" => "f64".into(),
45        "string" => "String".into(),
46        "bytes" => "::bytes::Bytes".into(),
47        "records" => "crate::records::RecordsPayload".into(),
48        "uuid" => "crate::primitives::uuid::Uuid".into(),
49        other => struct_path.map_or_else(|| panic!("unmapped owned type: {other}"), str::to_owned),
50    }
51}
52
53fn inner_borrowed(t: &str, struct_path: Option<&str>) -> String {
54    if let Some(elem) = t.strip_prefix("[]") {
55        return format!("Vec<{}>", inner_borrowed(elem, struct_path));
56    }
57    match t {
58        "bool" => "bool".into(),
59        "int8" => "i8".into(),
60        "int16" => "i16".into(),
61        "int32" => "i32".into(),
62        "int64" => "i64".into(),
63        "uint16" => "u16".into(),
64        "uint32" => "u32".into(),
65        "float64" => "f64".into(),
66        "string" => "&'a str".into(),
67        "bytes" => "&'a [u8]".into(),
68        "records" => "crate::records::RecordsPayloadBorrowed<'a>".into(),
69        "uuid" => "crate::primitives::uuid::Uuid".into(),
70        other => struct_path.map_or_else(
71            || panic!("unmapped borrowed type: {other}"),
72            // The caller is responsible for including `<'a>` in the path when needed.
73            // Pass the path verbatim.
74            str::to_owned,
75        ),
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use assert2::{assert, check};
82
83    use super::*;
84
85    #[test]
86    fn primitives_owned() {
87        for (t, nullable, want) in [
88            ("int16", false, "i16"),
89            ("int32", true, "Option<i32>"),
90            ("string", false, "String"),
91            ("string", true, "Option<String>"),
92            ("bytes", false, "::bytes::Bytes"),
93            ("uuid", false, "crate::primitives::uuid::Uuid"),
94            ("records", false, "crate::records::RecordsPayload"),
95        ] {
96            assert!(owned_type(t, nullable, None) == want);
97        }
98    }
99
100    #[test]
101    fn primitives_borrowed() {
102        for (t, nullable, want) in [
103            ("string", false, "&'a str"),
104            ("bytes", true, "Option<&'a [u8]>"),
105            (
106                "records",
107                false,
108                "crate::records::RecordsPayloadBorrowed<'a>",
109            ),
110        ] {
111            assert!(borrowed_type(t, nullable, None) == want);
112        }
113    }
114
115    #[test]
116    fn arrays() {
117        check!(owned_type("[]int32", false, None) == "Vec<i32>");
118        check!(owned_type("[]string", true, None) == "Option<Vec<String>>");
119        check!(borrowed_type("[]string", false, None) == "Vec<&'a str>");
120    }
121
122    #[test]
123    fn struct_refs() {
124        check!(owned_type("ProduceTopic", false, Some("ProduceTopic")) == "ProduceTopic");
125        // Caller is responsible for including <'a> in struct_path when needed.
126        check!(
127            borrowed_type("ProduceTopic", false, Some("ProduceTopic<'a>")) == "ProduceTopic<'a>"
128        );
129        check!(borrowed_type("ProduceTopic", false, Some("ProduceTopic")) == "ProduceTopic");
130        check!(owned_type("[]ProduceTopic", false, Some("ProduceTopic")) == "Vec<ProduceTopic>");
131        check!(
132            borrowed_type("[]ProduceTopic", false, Some("ProduceTopic<'a>"))
133                == "Vec<ProduceTopic<'a>>"
134        );
135        check!(borrowed_type("[]ProduceTopic", false, Some("ProduceTopic")) == "Vec<ProduceTopic>");
136    }
137}