xidl-parser 0.72.0

A IDL codegen.
Documentation
mod attrs;
mod context;
mod hash;
mod ops;
mod render;
mod types;

use super::*;
use crate::error::ParserResult;
use context::{ConstContext, TemplateContext};
use hash::{exception_hash_const_name, hash_string, op_hash_const_name};
use ops::{OperationInfo, collect_operations};
use std::collections::HashSet;

pub trait ToTemplateContext {
    fn to_template_context(&self, modules: &[String]) -> ParserResult<Option<TemplateContext>>;
}

impl ToTemplateContext for InterfaceDcl {
    fn to_template_context(&self, modules: &[String]) -> ParserResult<Option<TemplateContext>> {
        let InterfaceDclInner::InterfaceDef(def) = &self.decl else {
            return Ok(None);
        };
        let body = match def.interface_body.as_ref() {
            Some(body) => body,
            None => return Ok(None),
        };

        let ops = collect_operations(body);
        if ops.is_empty() {
            return Ok(None);
        }

        let consts = collect_consts(&def.header.ident, modules, &ops);
        let operations = ops.iter().map(|op| op.to_context()).collect::<Vec<_>>();

        Ok(Some(TemplateContext {
            modules: modules.to_vec(),
            interface_name: def.header.ident.clone(),
            consts,
            operations,
        }))
    }
}

pub fn expand_interface(
    interface: &InterfaceDcl,
    modules: &[String],
) -> ParserResult<Vec<Definition>> {
    let Some(ctx) = interface.to_template_context(modules)? else {
        return Ok(Vec::new());
    };

    let idl = render::render_template("interface.idl.j2", &ctx)?;
    let typed = crate::parser::parser_text(&idl)?;
    let spec = super::spec::spec_from_typed_ast(typed, false);
    Ok(spec.0)
}

fn collect_consts(
    interface_name: &str,
    modules: &[String],
    ops: &[OperationInfo],
) -> Vec<ConstContext> {
    let mut consts = Vec::new();
    let mut seen_exception_consts = HashSet::new();

    for op in ops {
        let op_hash_name = op_hash_const_name(interface_name, &op.name);
        consts.push(ConstContext {
            name: op_hash_name,
            value: hash_string(&op.name).to_string(),
        });

        for exception in &op.raises {
            let const_name = exception_hash_const_name(exception);
            if !seen_exception_consts.insert(const_name.clone()) {
                continue;
            }
            let full_name = types::qualified_exception_name(exception, modules);
            consts.push(ConstContext {
                name: const_name,
                value: hash_string(&full_name).to_string(),
            });
        }
    }

    consts
}

#[cfg(test)]
mod tests;