alef 0.115.5

Opinionated polyglot binding generator for Rust libraries
Documentation
use std::collections::HashMap;

use super::optional_renderers::{push_key_field_name, push_key_index_suffix};
use super::parse::segment_name;
use super::python_renderer::PythonAccessStep;
use super::types::{FieldResolver, PathSegment};

impl FieldResolver {
    pub(super) fn python_element_access_steps(&self, element: &str, array: &str) -> HashMap<String, PythonAccessStep> {
        let indexed = format!("{array}[0]");
        let prefix = format!("{}.", super::parse::normalize_numeric_indices(&indexed));
        let full = super::parse::parse_path(&format!("{indexed}.{element}"));
        self.python_access_steps(&full)
            .into_iter()
            .filter_map(|(key, step)| key.strip_prefix(&prefix).map(|relative| (relative.to_string(), step)))
            .collect()
    }

    pub(super) fn python_access_steps(&self, segments: &[PathSegment]) -> HashMap<String, PythonAccessStep> {
        let crossings: HashMap<_, _> = self
            .variant_crossings(segments)
            .into_iter()
            .map(|crossing| (crossing.key.clone(), crossing))
            .collect();
        let mut steps = HashMap::new();
        let mut cursor = self.ir_enum_map.root_type.clone();
        let mut key = String::new();
        for segment in segments {
            let Some(name) = segment_name(segment) else { continue };
            push_key_field_name(&mut key, segment);
            if let Some(crossing) = crossings.get(&key) {
                steps.insert(key.clone(), crossing.python_step());
                cursor = crossing.output_type.clone();
            } else if let Some(owner) = cursor.take() {
                if let Some(wire) = self
                    .ir_enum_map
                    .struct_wire_fields
                    .get(&owner)
                    .and_then(|fields| fields.get(name))
                {
                    steps.insert(
                        key.clone(),
                        PythonAccessStep {
                            native_suffix: None,
                            json_keys: vec![wire.clone()],
                            kind: self
                                .ir_enum_map
                                .struct_wire_kinds
                                .get(&owner)
                                .and_then(|fields| fields.get(name))
                                .copied()
                                .unwrap_or_default(),
                            starts_json: false,
                        },
                    );
                }
                cursor = self.python_next_owner(segment, &owner, name);
            }
            push_key_index_suffix(&mut key, segment);
        }
        steps
    }
    fn python_next_owner(&self, segment: &PathSegment, owner: &str, name: &str) -> Option<String> {
        let map_value = matches!(segment, PathSegment::MapAccess { .. })
            .then(|| {
                self.python_map_value_edges
                    .get(owner)
                    .and_then(|fields| fields.get(name))
            })
            .flatten();
        map_value
            .or_else(|| {
                self.ir_enum_map
                    .field_types
                    .get(owner)
                    .and_then(|fields| fields.get(name))
                    .or_else(|| {
                        self.ir_enum_map
                            .enum_field_types
                            .get(owner)
                            .and_then(|fields| fields.get(name))
                    })
            })
            .cloned()
    }
}

pub(super) fn json_keys<'a>(keys: impl IntoIterator<Item = &'a str>) -> String {
    keys.into_iter()
        .map(|key| {
            let quoted = serde_json::to_string(key).expect("JSON key is serializable");
            format!("[{quoted}]")
        })
        .collect()
}