alef_codegen/generators/
enums.rs1use crate::generators::RustBindingConfig;
2use alef_core::ir::EnumDef;
3use alef_core::keywords::PYTHON_KEYWORDS;
4use std::fmt::Write;
5
6pub fn enum_has_data_variants(enum_def: &EnumDef) -> bool {
9 enum_def.variants.iter().any(|v| !v.fields.is_empty())
10}
11
12fn enum_has_sanitized_fields(enum_def: &EnumDef) -> bool {
22 enum_def.variants.iter().any(|v| v.fields.iter().any(|f| f.sanitized))
23}
24
25pub fn gen_pyo3_data_enum(enum_def: &EnumDef, core_import: &str) -> String {
38 let name = &enum_def.name;
39 let core_path = crate::conversions::core_enum_path(enum_def, core_import);
40 let has_sanitized = enum_has_sanitized_fields(enum_def);
41 let mut out = String::with_capacity(512);
42
43 writeln!(out, "#[derive(Clone)]").ok();
44 writeln!(out, "#[pyclass(frozen)]").ok();
45 writeln!(out, "pub struct {name} {{").ok();
46 writeln!(out, " pub(crate) inner: {core_path},").ok();
47 writeln!(out, "}}").ok();
48 writeln!(out).ok();
49
50 writeln!(out, "#[pymethods]").ok();
51 writeln!(out, "impl {name} {{").ok();
52 if has_sanitized {
53 writeln!(out, "}}").ok();
57 } else {
58 writeln!(out, " #[new]").ok();
59 writeln!(
60 out,
61 " fn new(py: Python<'_>, value: &Bound<'_, pyo3::types::PyDict>) -> PyResult<Self> {{"
62 )
63 .ok();
64 writeln!(out, " let json_mod = py.import(\"json\")?;").ok();
65 writeln!(
66 out,
67 " let json_str: String = json_mod.call_method1(\"dumps\", (value,))?.extract()?;"
68 )
69 .ok();
70 writeln!(out, " let inner: {core_path} = serde_json::from_str(&json_str)").ok();
71 writeln!(
72 out,
73 " .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!(\"Invalid {name}: {{e}}\")))?;"
74 )
75 .ok();
76 writeln!(out, " Ok(Self {{ inner }})").ok();
77 writeln!(out, " }}").ok();
78 writeln!(out, "}}").ok();
79 }
80 writeln!(out).ok();
81
82 writeln!(out, "impl From<{name}> for {core_path} {{").ok();
84 writeln!(out, " fn from(val: {name}) -> Self {{ val.inner }}").ok();
85 writeln!(out, "}}").ok();
86 writeln!(out).ok();
87
88 writeln!(out, "impl From<{core_path}> for {name} {{").ok();
90 writeln!(out, " fn from(val: {core_path}) -> Self {{ Self {{ inner: val }} }}").ok();
91 writeln!(out, "}}").ok();
92 writeln!(out).ok();
93
94 writeln!(out, "impl serde::Serialize for {name} {{").ok();
97 writeln!(
98 out,
99 " fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {{"
100 )
101 .ok();
102 writeln!(out, " self.inner.serialize(serializer)").ok();
103 writeln!(out, " }}").ok();
104 writeln!(out, "}}").ok();
105 writeln!(out).ok();
106
107 writeln!(out, "impl Default for {name} {{").ok();
110 writeln!(
111 out,
112 " fn default() -> Self {{ Self {{ inner: Default::default() }} }}"
113 )
114 .ok();
115 writeln!(out, "}}").ok();
116 writeln!(out).ok();
117
118 writeln!(out, "impl<'de> serde::Deserialize<'de> for {name} {{").ok();
121 writeln!(
122 out,
123 " fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {{"
124 )
125 .ok();
126 writeln!(out, " let inner = {core_path}::deserialize(deserializer)?;").ok();
127 writeln!(out, " Ok(Self {{ inner }})").ok();
128 writeln!(out, " }}").ok();
129 writeln!(out, "}}").ok();
130
131 out
132}
133
134pub fn gen_enum(enum_def: &EnumDef, cfg: &RustBindingConfig) -> String {
136 let mut out = String::with_capacity(512);
140 let mut derives: Vec<&str> = cfg.enum_derives.to_vec();
141 derives.push("Default");
145 derives.push("serde::Serialize");
146 derives.push("serde::Deserialize");
147 if !derives.is_empty() {
148 writeln!(out, "#[derive({})]", derives.join(", ")).ok();
149 }
150 for attr in cfg.enum_attrs {
151 writeln!(out, "#[{attr}]").ok();
152 }
153 let is_pyo3 = cfg.enum_attrs.iter().any(|a| a.contains("pyclass"));
156 writeln!(out, "pub enum {} {{", enum_def.name).ok();
157 for (idx, variant) in enum_def.variants.iter().enumerate() {
158 if is_pyo3 && PYTHON_KEYWORDS.contains(&variant.name.as_str()) {
159 writeln!(out, " #[pyo3(name = \"{}_\")]", variant.name).ok();
160 }
161 if idx == 0 {
163 writeln!(out, " #[default]").ok();
164 }
165 writeln!(out, " {} = {idx},", variant.name).ok();
166 }
167 writeln!(out, "}}").ok();
168
169 out
170}