cxx 0.1.0

Safe interop between Rust and C++
Documentation
use crate::syntax::atom::Atom::{self, *};
use crate::syntax::set::OrderedSet as Set;
use crate::syntax::{Api, Derive, ExternType, Struct, Type};
use proc_macro2::Ident;
use quote::quote;
use std::collections::BTreeMap as Map;
use syn::{Error, Result};

pub struct Types<'a> {
    pub all: Set<'a, Type>,
    pub structs: Map<Ident, &'a Struct>,
    pub cxx: Set<'a, Ident>,
    pub rust: Set<'a, Ident>,
}

impl<'a> Types<'a> {
    pub fn collect(apis: &'a [Api]) -> Result<Self> {
        let mut all = Set::new();
        let mut structs = Map::new();
        let mut cxx = Set::new();
        let mut rust = Set::new();

        fn visit<'a>(all: &mut Set<'a, Type>, ty: &'a Type) {
            all.insert(ty);
            match ty {
                Type::Ident(_) | Type::Str(_) => {}
                Type::RustBox(ty) | Type::UniquePtr(ty) => visit(all, &ty.inner),
                Type::Ref(r) => visit(all, &r.inner),
            }
        }

        for api in apis {
            match api {
                Api::Include(_) => {}
                Api::Struct(strct) => {
                    let ident = &strct.ident;
                    if structs.contains_key(ident) || cxx.contains(ident) || rust.contains(ident) {
                        return Err(duplicate_struct(strct));
                    }
                    structs.insert(strct.ident.clone(), strct);
                    for field in &strct.fields {
                        visit(&mut all, &field.ty);
                    }
                }
                Api::CxxType(ety) => {
                    let ident = &ety.ident;
                    if structs.contains_key(ident) || cxx.contains(ident) || rust.contains(ident) {
                        return Err(duplicate_type(ety));
                    }
                    cxx.insert(ident);
                }
                Api::RustType(ety) => {
                    let ident = &ety.ident;
                    if structs.contains_key(ident) || cxx.contains(ident) || rust.contains(ident) {
                        return Err(duplicate_type(ety));
                    }
                    rust.insert(ident);
                }
                Api::CxxFunction(efn) | Api::RustFunction(efn) => {
                    for arg in &efn.args {
                        visit(&mut all, &arg.ty);
                    }
                    if let Some(ret) = &efn.ret {
                        visit(&mut all, ret);
                    }
                }
            }
        }

        Ok(Types {
            all,
            structs,
            cxx,
            rust,
        })
    }

    pub fn needs_indirect_abi(&self, ty: &Type) -> bool {
        match ty {
            Type::Ident(ident) => {
                if let Some(strct) = self.structs.get(ident) {
                    !self.is_pod(strct)
                } else {
                    Atom::from(ident) == Some(RustString)
                }
            }
            _ => false,
        }
    }

    pub fn is_pod(&self, strct: &Struct) -> bool {
        for derive in &strct.derives {
            if *derive == Derive::Copy {
                return true;
            }
        }
        false
    }
}

impl<'t, 'a> IntoIterator for &'t Types<'a> {
    type Item = &'a Type;
    type IntoIter = crate::syntax::set::Iter<'t, 'a, Type>;
    fn into_iter(self) -> Self::IntoIter {
        self.all.into_iter()
    }
}

fn duplicate_struct(strct: &Struct) -> Error {
    let struct_token = strct.struct_token;
    let ident = &strct.ident;
    let range = quote!(#struct_token #ident);
    Error::new_spanned(range, "duplicate type")
}

fn duplicate_type(ety: &ExternType) -> Error {
    let type_token = ety.type_token;
    let ident = &ety.ident;
    let range = quote!(#type_token #ident);
    Error::new_spanned(range, "duplicate type")
}