|
9 | 9 | //! same single source of truth the Python `_to_rust_*` converters use) and recurses through nested |
10 | 10 | //! DTOs, sequences, maps, and optionals so renamed fields at any depth survive the round-trip. |
11 | 11 |
|
| 12 | +use super::errors::is_dataclass_backed_config; |
12 | 13 | use crate::codegen::generators::{ |
13 | | - coercible_payload, collect_variant_constructors, enum_has_data_variants, pyo3_wire_schema_const_name, |
| 14 | + PYO3_DTO_COERCE_HELPER, coercible_payload, collect_variant_constructors, data_enum_needs_dto_coercion, |
| 15 | + enum_has_data_variants, pyo3_wire_schema_const_name, |
14 | 16 | }; |
15 | 17 | use crate::codegen::naming::wire_field_name; |
16 | 18 | use crate::codegen::shared::binding_fields; |
| 19 | +use crate::core::config::ResolvedCrateConfig; |
17 | 20 | use crate::core::ir::{ApiSurface, TypeDef}; |
18 | 21 | use ahash::{AHashMap, AHashSet}; |
19 | 22 |
|
| 23 | +/// Classify the dataclass-backed config-DTO type names: their public name resolves to a |
| 24 | +/// `@dataclass`/`dict` (via `options.py`), not the compiled `#[pyclass]`. Enum-variant payload |
| 25 | +/// fields of these types must accept the public wrapper or a dict — coerced into the core type — |
| 26 | +/// for parity with struct-field `_to_rust_*` coercion. Native-return types stay compiled and are |
| 27 | +/// left untouched. Same source of truth as `gen_init_py`'s import routing. |
| 28 | +pub(super) fn coercible_dto_names<'a>(api: &'a ApiSurface, config: &ResolvedCrateConfig) -> AHashSet<&'a str> { |
| 29 | + let output_style = config.dto.python_output_style(); |
| 30 | + let reexported: AHashSet<&str> = config |
| 31 | + .python |
| 32 | + .as_ref() |
| 33 | + .map(|p| p.reexported_types.iter().map(String::as_str).collect()) |
| 34 | + .unwrap_or_default(); |
| 35 | + api.types |
| 36 | + .iter() |
| 37 | + .filter(|t| is_dataclass_backed_config(t, output_style, &reexported)) |
| 38 | + .map(|t| t.name.as_str()) |
| 39 | + .collect() |
| 40 | +} |
| 41 | + |
| 42 | +/// Emit the data-enum DTO-coercion section for the generated pyo3 module: the runtime coercion |
| 43 | +/// helper ([`PYO3_DTO_COERCE_HELPER`]) followed by the per-DTO `__ALEF_WIRE_*` rename-schema consts. |
| 44 | +/// Returns an empty string when no data-enum variant constructor needs coercion (or `serde` is |
| 45 | +/// unavailable — the helper deserializes the coerced JSON into the core type). The helper and the |
| 46 | +/// schema consts are joined the same way `RustFileBuilder` joins items (`"\n\n"`), so emitting this |
| 47 | +/// as a single item is byte-identical to emitting them separately. |
| 48 | +pub(super) fn emit_dto_coercion_section(api: &ApiSurface, has_serde: bool, coercible: &AHashSet<&str>) -> String { |
| 49 | + let needed = has_serde && api.enums.iter().any(|e| data_enum_needs_dto_coercion(e, coercible)); |
| 50 | + if !needed { |
| 51 | + return String::new(); |
| 52 | + } |
| 53 | + let schema_consts = gen_wire_schema_consts(api, coercible); |
| 54 | + if schema_consts.is_empty() { |
| 55 | + PYO3_DTO_COERCE_HELPER.to_string() |
| 56 | + } else { |
| 57 | + format!("{PYO3_DTO_COERCE_HELPER}\n\n{schema_consts}") |
| 58 | + } |
| 59 | +} |
| 60 | + |
20 | 61 | /// One emitted `__AlefAlias` row. |
21 | 62 | struct AliasEntry { |
22 | 63 | rust: String, |
|
0 commit comments