use crate::parser::types::{Binding, Dec, FuncType, IDLType, PrimType, TypeField};
use crate::IDLProg;
pub mod rust;
pub use rust::idl_to_rust;
type Result<T = ()> = crate::error::Result<T>;
pub trait LanguageBinding {
fn start(&self) -> Result<()> {
Ok(())
}
fn done(&self, output: String) -> Result<String> {
Ok(output)
}
fn header(&self) -> Result<String> {
Ok(String::new())
}
fn footer(&self) -> Result<String> {
Ok(String::new())
}
fn usage(&self, ty: &IDLType) -> Result<String> {
match ty {
IDLType::PrimT(prim) => self.usage_prim(prim),
IDLType::VarT(var) => self.usage_var(var),
IDLType::FuncT(func) => self.usage_func(func),
IDLType::OptT(sub_t) => self.usage_opt(sub_t.as_ref()),
IDLType::VecT(item_t) => self.usage_vec(item_t.as_ref()),
IDLType::RecordT(fields) => self.usage_record(fields),
IDLType::VariantT(fields) => self.usage_variant(fields),
IDLType::ServT(serv_t) => self.usage_service(serv_t),
IDLType::ClassT(_, _) => unreachable!(),
IDLType::PrincipalT => Ok("principal".to_string()),
}
}
fn usage_prim(&self, ty: &PrimType) -> Result<String>;
fn usage_var(&self, var: &str) -> Result<String>;
fn usage_func(&self, func: &FuncType) -> Result<String>;
fn usage_opt(&self, ty: &IDLType) -> Result<String>;
fn usage_vec(&self, ty: &IDLType) -> Result<String>;
fn usage_record(&self, fields: &[TypeField]) -> Result<String>;
fn usage_variant(&self, fields: &[TypeField]) -> Result<String>;
fn usage_service(&self, ty: &[Binding]) -> Result<String>;
fn declare(&self, id: &str, ty: &IDLType) -> Result<String> {
match ty {
IDLType::PrimT(prim) => self.declare_prim(id, prim),
IDLType::VarT(var) => self.declare_var(id, var),
IDLType::FuncT(func) => self.declare_func(id, func),
IDLType::OptT(sub_t) => self.declare_opt(id, sub_t.as_ref()),
IDLType::VecT(item_t) => self.declare_vec(id, item_t.as_ref()),
IDLType::RecordT(fields) => self.declare_record(id, fields),
IDLType::VariantT(fields) => self.declare_variant(id, fields),
IDLType::ServT(serv_t) => self.declare_service(id, serv_t),
IDLType::ClassT(_, _) => unreachable!(),
IDLType::PrincipalT => Ok("principal".to_string()),
}
}
fn declare_prim(&self, id: &str, ty: &PrimType) -> Result<String>;
fn declare_var(&self, id: &str, var: &str) -> Result<String>;
fn declare_func(&self, id: &str, func: &FuncType) -> Result<String>;
fn declare_opt(&self, id: &str, ty: &IDLType) -> Result<String>;
fn declare_vec(&self, id: &str, ty: &IDLType) -> Result<String>;
fn declare_record(&self, id: &str, fields: &[TypeField]) -> Result<String>;
fn declare_variant(&self, id: &str, fields: &[TypeField]) -> Result<String>;
fn declare_service(&self, id: &str, ty: &[Binding]) -> Result<String>;
fn declaration_import(&self, _module: &str) -> Result<String> {
Ok(String::new())
}
fn declaration_binding(&self, binding: &Binding) -> Result<String> {
self.declare(&binding.id, &binding.typ)
}
fn declarations(&self, declarations: &[Dec]) -> Result<String> {
Ok(declarations
.iter()
.map(|d| match d {
Dec::ImportD(module) => self.declaration_import(module),
Dec::TypD(binding) => self.declaration_binding(binding),
})
.collect::<Result<Vec<String>>>()?
.join("\n"))
}
fn service(&self, bindings: &[Binding]) -> Result<String> {
bindings
.iter()
.map(|Binding { id, typ }| match typ {
IDLType::FuncT(func_t) => self.service_binding(id, func_t),
_ => self.usage(typ),
})
.collect::<Result<Vec<String>>>()
.map(|s| s.join("\n"))
}
fn service_binding(&self, id: &str, typ: &FuncType) -> Result<String>;
fn prog(&self, prog: &IDLProg) -> Result<String> {
self.start()?;
let output = self.header()?
+ &self.declarations(&prog.decs)?
+ &match &prog.actor {
None => String::new(),
Some(IDLType::ServT(bindings)) => self.service(bindings)?,
Some(IDLType::VarT(_)) => unimplemented!(),
_ => unreachable!(),
}
+ &self.footer()?;
self.done(output)
}
}
pub fn generate_code<Binding: LanguageBinding>(
prog: &IDLProg,
language_bindings: Binding,
) -> Result<String> {
language_bindings.prog(prog)
}