use std::collections::HashSet;
use anyhow::{anyhow, Context as _};
use const_format::formatcp;
use super::{
Context,
Parser,
EXTERN_EXCEPTION_TRAIT_MODULE_NAME,
RUST_MODULE_NAME,
TRAITS_MODULE_NAME,
};
use crate::model::r#enum::Enum;
use crate::model::Module;
impl Parser<syn::ItemMod> for Module {
fn parse(data: &syn::ItemMod, context: &mut Context) -> anyhow::Result<Self> {
let syn_enums = get_enums_from_module(data, context)?;
let enums: HashSet<Enum> = syn_enums
.iter()
.map(|item| Parser::parse(*item, context))
.collect::<Result<_, _>>()?;
if enums.len() != syn_enums.len() {
return Err(anyhow!("Duplicated enums found"));
}
let syn_exception_trait =
get_extern_mod_from_module(data, context, EXTERN_EXCEPTION_TRAIT_MODULE_NAME).context(
formatcp!("Cant find extern mod `{EXTERN_EXCEPTION_TRAIT_MODULE_NAME}`"),
)?;
let exception_trait = Parser::parse(&syn_exception_trait.items, context)
.context(formatcp!("Error in `{EXTERN_EXCEPTION_TRAIT_MODULE_NAME}`"))?;
let syn_traits_module = get_extern_mod_from_module(data, context, TRAITS_MODULE_NAME)
.context(formatcp!("Cant find extern mod `{TRAITS_MODULE_NAME}`"))?;
let traits_module = Parser::parse(&syn_traits_module.items, context)
.context(formatcp!("Error in `{TRAITS_MODULE_NAME}`"))?;
let syn_rust_module = get_extern_mod_from_module(data, context, RUST_MODULE_NAME)
.context(formatcp!("Cant find extern mod `{RUST_MODULE_NAME}`"))?;
let rust_module = Parser::parse(&syn_rust_module.items, context)
.context(formatcp!("Error in `{RUST_MODULE_NAME}`"))?;
let unknown = context.name_tracker.get_unknown();
if !unknown.is_empty() {
return Err(anyhow!("Some types are unknown: {unknown:?}"));
}
Ok(Self {
enums,
exception_trait,
traits_module,
rust_module,
})
}
}
fn get_enums_from_module<'a>(
module: &'a syn::ItemMod,
context: &Context,
) -> anyhow::Result<Vec<&'a syn::ItemEnum>> {
Ok(module
.content
.as_ref()
.context("The module is empty")?
.1
.iter()
.filter_map(|item| match item {
syn::Item::Enum(enum_item) => Some(enum_item),
_ => None,
})
.filter(|item| context.build_context.check_cfg_attrs(&item.attrs))
.collect())
}
fn get_extern_mod_from_module<'a>(
module: &'a syn::ItemMod,
context: &Context,
extern_lang: &str,
) -> Option<&'a syn::ItemForeignMod> {
module.content.as_ref().and_then(|(_, items)| {
items.iter().find_map(|module_item| match module_item {
syn::Item::ForeignMod(
rust_module @ syn::ItemForeignMod {
abi:
syn::Abi {
name: Some(lang), ..
},
..
},
) if lang.value() == extern_lang
&& context.build_context.check_cfg_attrs(&rust_module.attrs) =>
{
Some(rust_module)
}
_ => None,
})
})
}