dawn-codegen 0.1.2

Code generator for Dawn WebGPU API
Documentation
use crate::api_model::ApiModel;
use crate::api_model::StructureModel;
use std::collections::HashMap;

use crate::emitter::core::{build_stype_map, enum_variant_name_camel, type_name};

pub(crate) fn emit(model: &ApiModel, c_prefix: &str) -> String {
    emit_extensions(model, c_prefix)
}

pub(crate) fn emit_extensions(model: &ApiModel, c_prefix: &str) -> String {
    let mut out = String::new();
    out.push_str(&format!(
        r#"#![allow(dead_code, unused_imports)]

use crate::ffi;
use crate::generated::*;
use std::any::Any;

pub(crate) struct ChainedStructStorage {{
    entries: Vec<Box<ffi::{prefix}ChainedStruct>>,
    buffers: Vec<Box<dyn Any>>,
    nested: Vec<ChainedStructStorage>,
}}

impl ChainedStructStorage {{
    pub(crate) fn new() -> Self {{
        Self {{
            entries: Vec::new(),
            buffers: Vec::new(),
            nested: Vec::new(),
        }}
    }}

    pub(crate) fn push(
        &mut self,
        s_type: ffi::{prefix}SType,
        next: *mut ffi::{prefix}ChainedStruct,
    ) -> *mut ffi::{prefix}ChainedStruct {{
        let mut node = Box::new(ffi::{prefix}ChainedStruct {{ next, sType: s_type }});
        let ptr = std::ptr::from_mut(node.as_mut());
        self.entries.push(node);
        ptr
    }}

    pub(crate) fn push_value<T: 'static>(&mut self, value: T) -> *const T {{
        let boxed = Box::new(value);
        let ptr = std::ptr::from_ref(boxed.as_ref());
        self.buffers.push(boxed);
        ptr
    }}

    pub(crate) fn push_value_mut<T: 'static>(&mut self, value: T) -> *mut T {{
        let mut boxed = Box::new(value);
        let ptr = std::ptr::from_mut(boxed.as_mut());
        self.buffers.push(boxed);
        ptr
    }}

    pub(crate) fn push_vec<T: 'static>(&mut self, value: Vec<T>) -> *const T {{
        let ptr = value.as_ptr();
        self.buffers.push(Box::new(value));
        ptr
    }}

    pub(crate) fn push_vec_mut<T: 'static>(&mut self, value: Vec<T>) -> *mut T {{
        let mut value = value;
        let ptr = value.as_mut_ptr();
        self.buffers.push(Box::new(value));
        ptr
    }}

    pub(crate) fn push_any<T: 'static>(&mut self, value: T) {{
        self.buffers.push(Box::new(value));
    }}

    pub(crate) fn push_storage(&mut self, storage: ChainedStructStorage) {{
        self.nested.push(storage);
    }}
}}

"#,
        prefix = c_prefix
    ));

    let mut roots: HashMap<String, Vec<&StructureModel>> = HashMap::new();
    let mut extensible_roots: Vec<String> = Vec::new();
    for s in &model.structures {
        if s.def.extensible.is_extensible() {
            extensible_roots.push(s.name.clone());
        }
        for root in &s.def.chain_roots {
            roots.entry(root.clone()).or_default().push(s);
        }
    }

    let mut root_names: Vec<String> = extensible_roots;
    root_names.sort();
    root_names.dedup();

    let stype_map = build_stype_map(model, c_prefix);

    for root in root_names {
        let variants = roots.get(&root).cloned().unwrap_or_default();
        let enum_name = format!("{}Extension", type_name(&root));

        let mut variant_lines = Vec::new();
        let mut from_lines = Vec::new();
        for s in variants.iter() {
            let ty = type_name(&s.name);
            variant_lines.push(format!(r#"{ty}({ty}),"#));
            from_lines.push(format!(
                r#"impl std::convert::From<{ty}> for {enum_name} {{
                    fn from(ext: {ty}) -> Self {{
                        {enum_name}::{ty}(ext)
                    }}
                }}"#
            ));
        }
        let variant_block = variant_lines.join("\n");
        let from_block = from_lines.join("\n");

        let has_variants = !variants.is_empty();
        let impl_block = if has_variants {
            format!(
                r#"impl {enum_name} {{
    pub(crate) fn push_chain(
        &self,
        storage: &mut ChainedStructStorage,
        next: *mut ffi::{prefix}ChainedStruct,
    ) -> *mut ffi::{prefix}ChainedStruct {{
        match self {{
{push_arms}
        }}
    }}
}}

"#,
                enum_name = enum_name,
                prefix = c_prefix,
                push_arms = {
                    let mut arms = Vec::new();
                    for s in variants.iter() {
                        let ty = type_name(&s.name);
                        let stype_const = stype_map.get(&s.name).cloned().unwrap_or_else(|| {
                            format!(
                                r#"{}::{}"#,
                                type_name("s type"),
                                enum_variant_name_camel(&s.name)
                            )
                        });
                        arms.push(format!(
                            r#"            {enum_name}::{ty}(value) => {{
                let (mut raw, storage_value) = value.to_ffi();
                raw.chain.sType = {stype_const}.into();
                raw.chain.next = next;
                storage.push_storage(storage_value);
                let raw_ptr = storage.push_value_mut(raw);
                raw_ptr.cast::<ffi::{prefix}ChainedStruct>()
            }}"#,
                            enum_name = enum_name,
                            ty = ty,
                            stype_const = stype_const,
                            prefix = c_prefix
                        ));
                    }
                    arms.join("\n")
                }
            )
        } else {
            format!(
                r#"impl {enum_name} {{
    pub(crate) fn push_chain(
        &self,
        storage: &mut ChainedStructStorage,
        next: *mut ffi::{prefix}ChainedStruct,
    ) -> *mut ffi::{prefix}ChainedStruct {{
        let _ = self;
        let _ = storage;
        next
    }}
}}

"#,
                enum_name = enum_name,
                prefix = c_prefix
            )
        };

        out.push_str(&format!(
            r#"#[allow(dead_code)]
pub enum {enum_name} {{
{variants}
}}

{from_block}

{impl_block}
"#,
            enum_name = enum_name,
            variants = variant_block,
            impl_block = impl_block
        ));
    }

    out
}