dawn-codegen 0.1.2

Code generator for Dawn WebGPU API
Documentation
use crate::api_model::{ApiModel, CallbackFunctionModel, FunctionModel, StructureModel};
use crate::emitter::core::is_char_string_list;
use crate::emitter::core::names::{enum_variant_name_camel, shouty_snake_case_name, type_name};
use std::collections::HashMap;

pub(crate) struct TypeIndex {
    objects: HashMap<String, ()>,
    enums: HashMap<String, ()>,
    bitmasks: HashMap<String, ()>,
    structs: HashMap<String, bool>,
    callback_infos: HashMap<String, ()>,
    structs_need_free: HashMap<String, ()>,
}

impl TypeIndex {
    pub(crate) fn new(model: &ApiModel) -> Self {
        let mut objects = HashMap::new();
        let mut enums = HashMap::new();
        let mut bitmasks = HashMap::new();
        let mut structs = HashMap::new();
        let mut callback_infos = HashMap::new();
        let mut structs_need_free = HashMap::new();

        for o in &model.objects {
            objects.insert(o.name.clone(), ());
        }
        for e in &model.enums {
            enums.insert(e.name.clone(), ());
        }
        for b in &model.bitmasks {
            bitmasks.insert(b.name.clone(), ());
        }
        for s in &model.structures {
            structs.insert(s.name.clone(), s.def.extensible.is_extensible());
            if struct_needs_free_members(s) {
                structs_need_free.insert(s.name.clone(), ());
            }
        }
        for c in &model.callback_infos {
            callback_infos.insert(c.name.clone(), ());
        }

        Self {
            objects,
            enums,
            bitmasks,
            structs,
            callback_infos,
            structs_need_free,
        }
    }

    pub(crate) fn is_object(&self, name: &str) -> bool {
        self.objects.contains_key(name)
    }

    pub(crate) fn is_enum(&self, name: &str) -> bool {
        self.enums.contains_key(name)
    }

    pub(crate) fn is_bitmask(&self, name: &str) -> bool {
        self.bitmasks.contains_key(name)
    }

    pub(crate) fn struct_extensible(&self, name: &str) -> Option<bool> {
        self.structs.get(name).copied()
    }

    pub(crate) fn is_callback_info(&self, name: &str) -> bool {
        self.callback_infos.contains_key(name)
    }

    pub(crate) fn struct_needs_free_members(&self, name: &str) -> bool {
        self.structs_need_free.contains_key(name)
    }
}
pub(crate) fn build_constant_map(model: &ApiModel) -> HashMap<String, String> {
    let mut map = HashMap::new();
    for c in &model.constants {
        map.insert(c.name.clone(), shouty_snake_case_name(&c.name));
    }
    map
}
pub(crate) fn build_constructor_map(model: &ApiModel) -> HashMap<String, FunctionModel> {
    let mut map = HashMap::new();
    for func in &model.functions {
        if let Some(obj_name) = func.name.strip_prefix("create ") {
            map.insert(obj_name.to_string(), func.clone());
        }
    }
    map
}
pub(crate) fn build_callback_info_map(model: &ApiModel) -> HashMap<String, String> {
    let mut map = HashMap::new();
    for info in &model.callback_infos {
        if let Some(callback_member) = info
            .def
            .members
            .iter()
            .find(|member| member.name == "callback")
        {
            map.insert(info.name.clone(), callback_member.member_type.clone());
        }
    }
    map
}
pub(crate) fn build_callback_function_map(
    model: &ApiModel,
) -> HashMap<String, CallbackFunctionModel> {
    let mut map = HashMap::new();
    for callback in &model.callback_functions {
        map.insert(callback.name.clone(), callback.clone());
    }
    map
}
pub(crate) fn build_callback_info_mode_map(model: &ApiModel) -> HashMap<String, bool> {
    let mut map = HashMap::new();
    for info in &model.callback_infos {
        let has_mode = info.def.members.iter().any(|member| member.name == "mode");
        map.insert(info.name.clone(), has_mode);
    }
    map
}
pub(crate) fn build_stype_map(model: &ApiModel, c_prefix: &str) -> HashMap<String, String> {
    let mut map = HashMap::new();
    let stype_enum_name = "s type";
    let enum_name = type_name(stype_enum_name);
    if let Some(stype_enum) = model.enums.iter().find(|e| e.name == stype_enum_name) {
        for value in &stype_enum.def.values {
            let variant = enum_variant_name_camel(&value.name);
            map.insert(value.name.clone(), format!(r#"{enum_name}::{variant}"#));
        }
    }
    let _ = c_prefix;
    map
}

fn struct_needs_free_members(s: &StructureModel) -> bool {
    let is_out = s.def.extensible.is_output()
        || s.def.chained.as_deref() == Some("out")
        || s.def.out == Some(true);
    if !is_out {
        return false;
    }
    s.def.members.iter().any(|member| {
        if member.member_type == "string view" || is_char_string_list(member) {
            return true;
        }
        if member.length.is_some() {
            return true;
        }
        !member.annotation.is_value()
    })
}