use std::borrow::Cow;
use anyhow::Result;
use askama::Template;
use heck::{CamelCase, MixedCase};
use serde::{Deserialize, Serialize};
use crate::interface::*;
use crate::MergeWith;
use super::webidl::{
ArgumentExt, CPPArgument, ConstructorExt, FieldExt, FunctionExt, MethodExt, ReturnBy, ThrowBy,
WebIDLType,
};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Config {
pub definition_prefix: Option<String>,
}
impl From<&ComponentInterface> for Config {
fn from(_ci: &ComponentInterface) -> Self {
Config::default()
}
}
impl MergeWith for Config {
fn merge_with(&self, other: &Self) -> Self {
Config {
definition_prefix: self.definition_prefix.merge_with(&other.definition_prefix),
}
}
}
#[derive(Clone, Copy)]
pub struct Context<'config, 'ci> {
config: &'config Config,
ci: &'ci ComponentInterface,
}
impl<'config, 'ci> Context<'config, 'ci> {
pub fn new(config: &'config Config, ci: &'ci ComponentInterface) -> Self {
Context { config, ci }
}
pub fn ffi_rustbuffer_type(&self) -> String {
format!("{}_RustBuffer", self.ci.ffi_namespace())
}
pub fn ffi_foreignbytes_type(&self) -> String {
format!("{}_ForeignBytes", self.ci.ffi_namespace())
}
pub fn ffi_rusterror_type(&self) -> String {
format!("{}_RustError", self.ci.ffi_namespace())
}
pub fn detail_name(&self) -> String {
format!("{}_detail", self.ci.namespace())
}
pub fn namespace(&self) -> &'ci str {
self.ci.namespace()
}
pub fn type_name<'a>(&self, ident: &'a str) -> Cow<'a, str> {
match self.config.definition_prefix.as_ref() {
Some(prefix) => Cow::Owned(format!("{}{}", prefix, ident)),
None => Cow::Borrowed(ident),
}
}
pub fn header_name(&self, ident: &str) -> String {
self.type_name(ident).to_camel_case()
}
}
#[derive(Template)]
#[template(syntax = "webidl", escape = "none", path = "WebIDLTemplate.webidl")]
pub struct WebIDL<'config, 'ci> {
context: Context<'config, 'ci>,
ci: &'ci ComponentInterface,
}
impl<'config, 'ci> WebIDL<'config, 'ci> {
pub fn new(context: Context<'config, 'ci>, ci: &'ci ComponentInterface) -> Self {
Self { context, ci }
}
}
#[derive(Template)]
#[template(syntax = "c", escape = "none", path = "SharedHeaderTemplate.h")]
pub struct SharedHeader<'config, 'ci> {
context: Context<'config, 'ci>,
ci: &'ci ComponentInterface,
}
impl<'config, 'ci> SharedHeader<'config, 'ci> {
pub fn new(context: Context<'config, 'ci>, ci: &'ci ComponentInterface) -> Self {
Self { context, ci }
}
}
#[derive(Template)]
#[template(syntax = "c", escape = "none", path = "NamespaceHeaderTemplate.h")]
pub struct NamespaceHeader<'config, 'ci> {
context: Context<'config, 'ci>,
functions: &'ci [Function],
}
impl<'config, 'ci> NamespaceHeader<'config, 'ci> {
pub fn new(context: Context<'config, 'ci>, functions: &'ci [Function]) -> Self {
Self { context, functions }
}
}
#[derive(Template)]
#[template(syntax = "cpp", escape = "none", path = "NamespaceTemplate.cpp")]
pub struct Namespace<'config, 'ci> {
context: Context<'config, 'ci>,
functions: &'ci [Function],
}
impl<'config, 'ci> Namespace<'config, 'ci> {
pub fn new(context: Context<'config, 'ci>, functions: &'ci [Function]) -> Self {
Self { context, functions }
}
}
#[derive(Template)]
#[template(syntax = "c", escape = "none", path = "InterfaceHeaderTemplate.h")]
pub struct InterfaceHeader<'config, 'ci> {
context: Context<'config, 'ci>,
obj: &'ci Object,
}
impl<'config, 'ci> InterfaceHeader<'config, 'ci> {
pub fn new(context: Context<'config, 'ci>, obj: &'ci Object) -> Self {
Self { context, obj }
}
}
#[derive(Template)]
#[template(syntax = "cpp", escape = "none", path = "InterfaceTemplate.cpp")]
pub struct Interface<'config, 'ci> {
context: Context<'config, 'ci>,
obj: &'ci Object,
}
impl<'config, 'ci> Interface<'config, 'ci> {
pub fn new(context: Context<'config, 'ci>, obj: &'ci Object) -> Self {
Self { context, obj }
}
}
mod filters {
use super::*;
pub fn type_webidl(
type_: &WebIDLType,
context: &Context<'_, '_>,
) -> Result<String, askama::Error> {
Ok(match type_ {
WebIDLType::Flat(Type::Int8) => "byte".into(),
WebIDLType::Flat(Type::UInt8) => "octet".into(),
WebIDLType::Flat(Type::Int16) => "short".into(),
WebIDLType::Flat(Type::UInt16) => "unsigned short".into(),
WebIDLType::Flat(Type::Int32) => "long".into(),
WebIDLType::Flat(Type::UInt32) => "unsigned long".into(),
WebIDLType::Flat(Type::Int64) => "long long".into(),
WebIDLType::Flat(Type::UInt64) => "unsigned long long".into(),
WebIDLType::Flat(Type::Float32) => "float".into(),
WebIDLType::Flat(Type::Float64) => "double".into(),
WebIDLType::Flat(Type::Boolean) => "boolean".into(),
WebIDLType::Flat(Type::String) => "DOMString".into(),
WebIDLType::Flat(Type::Enum(name))
| WebIDLType::Flat(Type::Record(name))
| WebIDLType::Flat(Type::Object(name)) => class_name_webidl(name, context)?,
WebIDLType::Nullable(inner) => format!("{}?", type_webidl(inner, context)?),
WebIDLType::Optional(inner) | WebIDLType::OptionalWithDefaultValue(inner) => {
type_webidl(inner, context)?
}
WebIDLType::Sequence(inner) => format!("sequence<{}>", type_webidl(inner, context)?),
WebIDLType::Map(inner) => {
format!("record<DOMString, {}>", type_webidl(inner, context)?)
}
_ => unreachable!("type_webidl({:?})", type_),
})
}
pub fn literal_webidl(literal: &Literal) -> Result<String, askama::Error> {
Ok(match literal {
Literal::Boolean(v) => format!("{}", v),
Literal::String(s) => format!("\"{}\"", s),
Literal::Null => "null".into(),
Literal::EmptySequence => "[]".into(),
Literal::EmptyMap => "{}".into(),
Literal::Enum(v, _) => format!("\"{}\"", enum_variant_webidl(v)?),
Literal::Int(i, radix, _) => match radix {
Radix::Octal => format!("0{:o}", i),
Radix::Decimal => format!("{}", i),
Radix::Hexadecimal => format!("{:#x}", i),
},
Literal::UInt(i, radix, _) => match radix {
Radix::Octal => format!("0{:o}", i),
Radix::Decimal => format!("{}", i),
Radix::Hexadecimal => format!("{:#x}", i),
},
Literal::Float(string, _) => string.into(),
})
}
pub fn type_ffi(type_: &FFIType, context: &Context<'_, '_>) -> Result<String, askama::Error> {
Ok(match type_ {
FFIType::Int8 => "int8_t".into(),
FFIType::UInt8 => "uint8_t".into(),
FFIType::Int16 => "int16_t".into(),
FFIType::UInt16 => "uint16_t".into(),
FFIType::Int32 => "int32_t".into(),
FFIType::UInt32 => "uint32_t".into(),
FFIType::Int64 => "int64_t".into(),
FFIType::UInt64 => "uint64_t".into(),
FFIType::Float32 => "float".into(),
FFIType::Float64 => "double".into(),
FFIType::RustCString => "const char*".into(),
FFIType::RustBuffer => context.ffi_rustbuffer_type(),
FFIType::RustError => context.ffi_rusterror_type(),
FFIType::ForeignBytes => context.ffi_foreignbytes_type(),
FFIType::ForeignCallback => unimplemented!("Callback interfaces are not implemented"),
})
}
pub fn type_cpp(
type_: &WebIDLType,
context: &Context<'_, '_>,
) -> Result<String, askama::Error> {
Ok(match type_ {
WebIDLType::Flat(Type::Int8) => "int8_t".into(),
WebIDLType::Flat(Type::UInt8) => "uint8_t".into(),
WebIDLType::Flat(Type::Int16) => "int16_t".into(),
WebIDLType::Flat(Type::UInt16) => "uint16_t".into(),
WebIDLType::Flat(Type::Int32) => "int32_t".into(),
WebIDLType::Flat(Type::UInt32) => "uint32_t".into(),
WebIDLType::Flat(Type::Int64) => "int64_t".into(),
WebIDLType::Flat(Type::UInt64) => "uint64_t".into(),
WebIDLType::Flat(Type::Float32) => "float".into(),
WebIDLType::Flat(Type::Float64) => "double".into(),
WebIDLType::Flat(Type::Boolean) => "bool".into(),
WebIDLType::Flat(Type::String) => "nsString".into(),
WebIDLType::Flat(Type::Enum(name)) | WebIDLType::Flat(Type::Record(name)) => {
class_name_cpp(name, context)?
}
WebIDLType::Flat(Type::Object(name)) => {
format!("OwningNonNull<{}>", class_name_cpp(name, context)?)
}
WebIDLType::Nullable(inner) => {
match inner.as_ref() {
WebIDLType::Flat(Type::Object(name)) => {
format!("RefPtr<{}>", class_name_cpp(name, context)?)
}
WebIDLType::Flat(Type::String) => "nsString".into(),
_ => format!("Nullable<{}>", type_cpp(inner, context)?),
}
}
WebIDLType::OptionalWithDefaultValue(inner) => type_cpp(inner, context)?,
WebIDLType::Optional(inner) => format!("Optional<{}>", type_cpp(inner, context)?),
WebIDLType::Sequence(inner) => format!("nsTArray<{}>", type_cpp(inner, context)?),
WebIDLType::Map(inner) => format!("Record<nsString, {}>", type_cpp(inner, context)?),
_ => unreachable!("type_cpp({:?})", type_),
})
}
fn in_arg_type_cpp(
type_: &WebIDLType,
context: &Context<'_, '_>,
) -> Result<String, askama::Error> {
Ok(match type_ {
WebIDLType::Nullable(inner) => match inner.as_ref() {
WebIDLType::Flat(Type::Object(_)) | WebIDLType::Flat(Type::String) => {
type_cpp(type_, context)?
}
_ => format!("Nullable<{}>", in_arg_type_cpp(inner, context)?),
},
WebIDLType::Sequence(inner) => {
format!("Sequence<{}>", in_arg_type_cpp(&inner, context)?)
}
_ => type_cpp(type_, context)?,
})
}
pub fn arg_type_cpp(
arg: &CPPArgument<'_>,
context: &Context<'_, '_>,
) -> Result<String, askama::Error> {
Ok(match arg {
CPPArgument::GlobalObject => "GlobalObject&".into(),
CPPArgument::ErrorResult => "ErrorResult&".into(),
CPPArgument::In(arg) => {
match arg.webidl_type() {
WebIDLType::Flat(Type::String) => "const nsAString&".into(),
WebIDLType::Flat(Type::Object(name)) => {
format!("{}&", class_name_cpp(&name, context)?)
}
WebIDLType::Nullable(inner) => match inner.as_ref() {
WebIDLType::Flat(Type::String) => "const nsAString&".into(),
WebIDLType::Flat(Type::Object(name)) => {
format!("{}*", class_name_cpp(&name, context)?)
}
_ => format!("const {}&", in_arg_type_cpp(&arg.webidl_type(), context)?),
},
WebIDLType::Flat(Type::Record(_))
| WebIDLType::Map(_)
| WebIDLType::Sequence(_)
| WebIDLType::Optional(_)
| WebIDLType::OptionalWithDefaultValue(_) => {
format!("const {}&", in_arg_type_cpp(&arg.webidl_type(), context)?)
}
_ => in_arg_type_cpp(&arg.webidl_type(), context)?,
}
}
CPPArgument::Out(type_) => {
match type_ {
WebIDLType::Flat(Type::String) => "nsAString&".into(),
WebIDLType::Nullable(inner) => match inner.as_ref() {
WebIDLType::Flat(Type::String) => "nsAString&".into(),
_ => format!("{}&", type_cpp(type_, context)?),
},
_ => format!("{}&", type_cpp(type_, context)?),
}
}
})
}
pub fn ret_type_cpp(
type_: &WebIDLType,
context: &Context<'_, '_>,
) -> Result<String, askama::Error> {
Ok(match type_ {
WebIDLType::Flat(Type::Object(name)) => {
format!("already_AddRefed<{}>", class_name_cpp(name, context)?)
}
WebIDLType::Nullable(inner) => match inner.as_ref() {
WebIDLType::Flat(Type::Object(name)) => {
format!("already_AddRefed<{}>", class_name_cpp(name, context)?)
}
_ => type_cpp(type_, context)?,
},
_ => type_cpp(type_, context)?,
})
}
pub fn dummy_ret_value_cpp(
return_type: &WebIDLType,
context: &Context<'_, '_>,
) -> Result<String, askama::Error> {
Ok(match return_type {
WebIDLType::Flat(Type::Int8)
| WebIDLType::Flat(Type::UInt8)
| WebIDLType::Flat(Type::Int16)
| WebIDLType::Flat(Type::UInt16)
| WebIDLType::Flat(Type::Int32)
| WebIDLType::Flat(Type::UInt32)
| WebIDLType::Flat(Type::Int64)
| WebIDLType::Flat(Type::UInt64) => "0".into(),
WebIDLType::Flat(Type::Float32) => "0.0f".into(),
WebIDLType::Flat(Type::Float64) => "0.0".into(),
WebIDLType::Flat(Type::Boolean) => "false".into(),
WebIDLType::Flat(Type::Enum(name)) => {
format!("{}::EndGuard_", class_name_cpp(name, context)?)
}
WebIDLType::Flat(Type::Object(_)) => "nullptr".into(),
WebIDLType::Flat(Type::String) => "EmptyString()".into(),
WebIDLType::OptionalWithDefaultValue(inner) => {
dummy_ret_value_cpp(inner.as_ref(), context)?
}
WebIDLType::Optional(_)
| WebIDLType::Nullable(_)
| WebIDLType::Flat(Type::Record(_))
| WebIDLType::Map(_)
| WebIDLType::Sequence(_) => format!("{}()", type_cpp(return_type, context)?),
_ => unreachable!("dummy_ret_value_cpp({:?})", return_type),
})
}
pub fn lower_cpp(
type_: &WebIDLType,
from: &str,
context: &Context<'_, '_>,
) -> Result<String, askama::Error> {
let (lifted, nullable) = match type_ {
WebIDLType::Flat(Type::String) => ("nsAString".into(), false),
WebIDLType::OptionalWithDefaultValue(_) => (type_cpp(type_, context)?, true),
WebIDLType::Nullable(inner) => match inner.as_ref() {
WebIDLType::Flat(Type::String) => ("nsAString".into(), true),
_ => (in_arg_type_cpp(type_, context)?, false),
},
_ => (in_arg_type_cpp(type_, context)?, false),
};
Ok(format!(
"{}::ViaFfi<{}, {}, {}>::Lower({})",
context.detail_name(),
lifted,
type_ffi(&FFIType::from(type_), context)?,
nullable,
from
))
}
pub fn lift_cpp(
type_: &WebIDLType,
from: &str,
into: &str,
context: &Context<'_, '_>,
) -> Result<String, askama::Error> {
let (lifted, nullable) = match type_ {
WebIDLType::Flat(Type::String) => ("nsAString".into(), false),
WebIDLType::OptionalWithDefaultValue(_) => (type_cpp(type_, context)?, true),
WebIDLType::Nullable(inner) => match inner.as_ref() {
WebIDLType::Flat(Type::String) => ("nsAString".into(), true),
_ => (type_cpp(type_, context)?, false),
},
_ => (type_cpp(type_, context)?, false),
};
Ok(format!(
"{}::ViaFfi<{}, {}, {}>::Lift({}, {})",
context.detail_name(),
lifted,
type_ffi(&FFIType::from(type_), context)?,
nullable,
from,
into,
))
}
pub fn var_name_webidl(nm: &str) -> Result<String, askama::Error> {
Ok(nm.to_mixed_case())
}
pub fn enum_variant_webidl(nm: &str) -> Result<String, askama::Error> {
Ok(nm.to_mixed_case())
}
pub fn header_name_cpp(nm: &str, context: &Context<'_, '_>) -> Result<String, askama::Error> {
Ok(context.header_name(nm))
}
pub fn class_name_webidl(nm: &str, context: &Context<'_, '_>) -> Result<String, askama::Error> {
Ok(context.type_name(nm).to_camel_case())
}
pub fn class_name_cpp(nm: &str, context: &Context<'_, '_>) -> Result<String, askama::Error> {
Ok(context.type_name(nm).to_camel_case())
}
pub fn fn_name_webidl(nm: &str) -> Result<String, askama::Error> {
Ok(nm.to_string().to_mixed_case())
}
pub fn fn_name_cpp(nm: &str) -> Result<String, askama::Error> {
Ok(nm.to_string().to_camel_case())
}
pub fn field_name_cpp(nm: &str) -> Result<String, askama::Error> {
Ok(format!("m{}", nm.to_camel_case()))
}
pub fn enum_variant_cpp(nm: &str) -> Result<String, askama::Error> {
Ok(nm.to_camel_case())
}
}