use std::fmt::Display;
use std::io;
use std::io::Write;
use std::path::Path;
use indoc::indoc;
use uuid::Uuid;
use crate::codegen::{Ctype, File, Function, Interface, InterfaceDefn, Toplevel};
pub fn write_cguid<W: Write>(mut w: W, f: &File, source: &Path) -> io::Result<()> {
writeln!(w, "// Auto-generated by nuidl from {}.", source.display())?;
writeln!(w, "#include <nucom/idldefs.h>")?;
writeln!(w)?;
for el in &f.elems {
if let Toplevel::Interface(itf) = el {
if let Some(InterfaceDefn {
uuid: Some(uuid), ..
}) = itf.defn
{
writeln!(w, "const GUID IID_{} = {};", itf.name, GuidRepr(uuid))?;
}
}
}
Ok(())
}
pub fn write_header<W: Write>(
mut w: W,
f: &File,
source: &Path,
guard_name: &str,
) -> io::Result<()> {
writeln!(
w,
indoc! {r#"
// Auto-generated by nuidl from {}.
#ifndef {1}
#define {1}
#include <nucom/idldefs.h>
"#},
source.display(),
guard_name,
)?;
for el in &f.imports {
writeln!(w, "#include \"{}\"", el.c_name.display())?;
}
for el in &f.elems {
if let Toplevel::Interface(itf) = el {
writeln!(w, "typedef struct {0} {0};", itf.name)?;
}
}
for el in &f.elems {
match el {
Toplevel::CppQuote(text) => {
writeln!(w, "{}", text)?;
}
Toplevel::Interface(i) => {
print_interface(&mut w, i)?;
}
}
}
writeln!(
w,
indoc! {r#"
#endif // {}
"#},
guard_name
)?;
Ok(())
}
fn write_type_space<W: Write>(t: &Ctype, mut w: W, with_space: bool) -> io::Result<()> {
write!(w, "{}", t.typename)?;
if t.is_const {
write!(w, " const")?;
}
let mut next_space = true;
for ptr in &t.indirection {
if next_space {
write!(w, " ")?;
}
write!(w, "*")?;
if ptr.is_const {
write!(w, "const")?;
}
next_space = ptr.is_const;
}
if next_space && with_space {
write!(w, " ")?;
}
Ok(())
}
fn write_type<W: Write>(t: &Ctype, w: W) -> io::Result<()> {
write_type_space(t, w, true)
}
struct GuidRepr(Uuid);
impl Display for GuidRepr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (d1, d2, d3, d4) = self.0.as_fields();
write!(f, "{{0x{:X},", d1)?;
write!(f, "0x{:X},", d2)?;
write!(f, "0x{:X},{{", d3)?;
for el in d4 {
write!(f, "0x{:X},", el)?;
}
write!(f, "}}}}")?;
Ok(())
}
}
fn print_interface<W: Write>(w: &mut W, i: &Interface) -> io::Result<()> {
if i.defn.is_none() {
return Ok(());
}
writeln!(
w,
indoc! {r#"
// interface {}
#if !defined(__cplusplus) || defined(CINTERFACE)
#if defined(__cplusplus)
extern "C" {{
#endif
"#},
i.name
)?;
print_c_interface(&mut *w, i)?;
writeln!(
w,
indoc! {r#"
#if defined(__cplusplus)
}}
#endif
#else
"#}
)?;
print_cpp_interface(&mut *w, i)?;
writeln!(
w,
indoc! {r#"
#endif
"#}
)?;
Ok(())
}
fn print_c_interface<W: Write>(w: &mut W, i: &Interface) -> io::Result<()> {
let defn = i.defn.as_ref().unwrap();
writeln!(w, "struct {}Vtbl\n{{\n BEGIN_INTERFACE", i.name)?;
writeln!(w)?;
print_c_functions(&mut *w, i, i)?;
writeln!(w, " END_INTERFACE\n}};")?;
writeln!(
w,
"struct {0}\n{{\n struct {0}Vtbl *lpVtbl;\n}};",
i.name
)?;
writeln!(w)?;
if defn.uuid.is_some() {
writeln!(w, "extern IID const IID_{};", i.name)?;
writeln!(w)?;
}
print_c_call_macros(&mut *w, i, i)?;
Ok(())
}
fn print_c_functions<W: Write>(w: &mut W, i: &Interface, cur: &Interface) -> io::Result<()> {
let cur_defn = cur.defn.as_ref().unwrap();
if let Some(base) = &cur_defn.base {
print_c_functions(&mut *w, i, base)?;
}
writeln!(w, " // {}\n", cur.name)?;
for f in &cur_defn.fns {
print_c_function(&mut *w, i, &f)?;
writeln!(w)?;
}
Ok(())
}
fn print_c_function<W: Write>(w: &mut W, i: &Interface, f: &Function) -> io::Result<()> {
write!(w, " ")?;
write_type(&f.ret, &mut *w)?;
write!(
w,
"(STDMETHODCALLTYPE *{})(\n {} *self",
f.name, i.name
)?;
for arg in &f.params {
write!(w, ",\n ")?;
write_type(&arg.ty, &mut *w)?;
if let Some(name) = &arg.name {
write!(w, "{}", **name)?;
}
}
writeln!(w, "\n );")?;
Ok(())
}
fn print_c_call_macros<W: Write>(w: &mut W, i: &Interface, cur: &Interface) -> io::Result<()> {
let cur_defn = cur.defn.as_ref().unwrap();
if let Some(base) = &cur_defn.base {
print_c_call_macros(&mut *w, i, base)?;
}
for f in &cur_defn.fns {
print_c_call_macro(&mut *w, i, &f)?;
}
Ok(())
}
fn print_c_call_macro<W: Write>(w: &mut W, i: &Interface, f: &Function) -> io::Result<()> {
write!(w, "#define {}_{}(self", i.name, f.name)?;
let write_params_block = |w: &mut W| {
for (idx, arg) in f.params.iter().enumerate() {
write!(w, ", ")?;
match &arg.name {
Some(name) => write!(w, "{}", **name)?,
_ => write!(w, "p{}", idx)?,
}
}
Ok::<(), io::Error>(())
};
write_params_block(&mut *w)?;
write!(w, ") (self->lpVtbl->{}(self", f.name)?;
write_params_block(&mut *w)?;
writeln!(w, "))")?;
Ok(())
}
fn print_cpp_interface<W: Write>(w: &mut W, i: &Interface) -> io::Result<()> {
let defn = i.defn.as_ref().unwrap();
write!(w, "struct {}", i.name)?;
if let Some(n) = &defn.base {
write!(w, " : public {}", n.name)?;
};
writeln!(w, "\n{{\n BEGIN_INTERFACE")?;
writeln!(w)?;
print_cpp_functions(&mut *w, i)?;
writeln!(w, " END_INTERFACE\n}};")?;
writeln!(w)?;
if let Some(guid) = defn.uuid {
write!(
w,
indoc! {r#"
extern "C" IID const IID_{};
namespace nucom {{
extern "C++" template<>
struct GuidOf<{0}>
{{
static constexpr GUID guid = {};
}};
}}
"#},
i.name,
GuidRepr(guid)
)?;
}
Ok(())
}
fn print_cpp_functions<W: Write>(w: &mut W, i: &Interface) -> io::Result<()> {
let InterfaceDefn { base, fns, .. } = i.defn.as_ref().unwrap();
let mut base = base;
let mut needs_space = false;
while let Some(Interface {
name: base_name,
defn:
Some(InterfaceDefn {
fns: base_fns,
base: base_base,
..
}),
..
}) = base.as_deref()
{
for f in base_fns {
if f.params.iter().any(|p| p.poly_type.is_some()) {
writeln!(w, " using {}::{};", base_name, f.name)?;
needs_space = true;
}
}
base = &base_base;
}
if needs_space {
writeln!(w)?;
}
for f in fns {
print_cpp_function(&mut *w, i, &f)?;
writeln!(w)?;
}
Ok(())
}
fn print_cpp_function<W: Write>(w: &mut W, _i: &Interface, f: &Function) -> io::Result<()> {
write!(w, " virtual ")?;
write_type(&f.ret, &mut *w)?;
write!(w, "STDMETHODCALLTYPE {}(", f.name)?;
for (idx, arg) in f.params.iter().enumerate() {
if idx > 0 {
write!(w, ",")?;
}
write!(w, "\n ")?;
write_type(&arg.ty, &mut *w)?;
if let Some(name) = &arg.name {
write!(w, "{}", **name)?;
}
}
if !f.params.is_empty() {
write!(w, "\n ")?;
}
writeln!(w, ") = 0;")?;
if f.params.iter().any(|p| p.poly_type.is_some()) {
writeln!(w)?;
#[derive(Debug)]
struct PolyTypeInfo {
value_pos: usize,
template_idx: usize,
}
let mut poly_types = Vec::new();
let mut type_idx = 1;
for (idx, param) in f.params.iter().enumerate() {
if let Some(_pt) = ¶m.poly_type {
poly_types.push(PolyTypeInfo {
value_pos: idx,
template_idx: type_idx,
});
type_idx += 1;
}
}
write!(w, " template <")?;
for (idx, pt) in poly_types.iter().enumerate() {
if idx > 0 {
write!(w, ", ")?;
}
write!(w, "typename T{}", pt.template_idx)?;
}
writeln!(w, ">")?;
write!(w, " inline ")?;
write_type(&f.ret, &mut *w)?;
write!(w, "{}(", f.name)?;
let new_params = f
.params
.iter()
.enumerate()
.filter(|(_, param)| param.poly_value.is_none());
let mut first = true;
for (idx, arg) in new_params {
if !first {
write!(w, ",")?;
}
first = false;
write!(w, "\n ")?;
if arg.poly_type.is_none() {
write_type(&arg.ty, &mut *w)?;
} else {
let pt = poly_types.iter().find(|pt| pt.value_pos == idx).unwrap();
let ty = Ctype {
typename: format!("T{}", pt.template_idx),
..arg.ty.inner().clone()
};
write_type(&ty, &mut *w)?;
}
if let Some(name) = &arg.name {
write!(w, "{}", **name)?;
} else {
write!(w, "unnamed_{}", idx)?;
}
}
write!(w, "\n ")?;
writeln!(w, ")")?;
writeln!(w, " {{")?;
write!(w, " return this->{}(", f.name)?;
for (idx, arg) in f.params.iter().enumerate() {
if idx > 0 {
write!(w, ",")?;
}
write!(w, "\n ")?;
if let Some(poly_value) = arg.poly_value {
let pt = poly_types
.iter()
.find(|pt| pt.value_pos == poly_value)
.unwrap();
write!(w, "&::nucom::guid_of<T{}>", pt.template_idx)?;
} else {
let needs_cast = arg.poly_type.is_some();
if needs_cast {
write!(w, "reinterpret_cast<")?;
write_type_space(&arg.ty, &mut *w, false)?;
write!(w, ">(")?;
}
if let Some(name) = &arg.name {
write!(w, "{}", **name)?;
} else {
write!(w, "unnamed_{}", idx)?;
}
if needs_cast {
write!(w, ")")?;
}
}
}
write!(w, "\n ")?;
writeln!(w, ");")?;
writeln!(w, " }}")?;
}
Ok(())
}