mod block;
mod builtin;
mod cfg;
mod check;
pub(super) mod error;
mod file;
pub(super) mod fs;
mod ifndef;
pub(super) mod include;
mod names;
mod namespace;
mod nested;
pub(super) mod out;
mod write;
use self::cfg::UnsupportedCfgEvaluator;
use self::error::{format_err, Result};
use self::file::File;
use self::include::Include;
use crate::syntax::cfg::CfgExpr;
use crate::syntax::report::Errors;
use crate::syntax::{self, attrs, Types};
use std::collections::BTreeSet as Set;
use std::path::Path;
pub(super) use self::error::Error;
#[non_exhaustive]
pub struct Opt {
    pub include: Vec<Include>,
    pub cxx_impl_annotations: Option<String>,
    pub cfg_evaluator: Box<dyn CfgEvaluator>,
    pub(super) gen_header: bool,
    pub(super) gen_implementation: bool,
    pub(super) allow_dot_includes: bool,
    pub(super) doxygen: bool,
}
pub trait CfgEvaluator {
    fn eval(&self, name: &str, value: Option<&str>) -> CfgResult;
}
pub enum CfgResult {
    True,
    False,
    Undetermined {
        msg: String,
    },
}
#[derive(Default)]
pub struct GeneratedCode {
    pub header: Vec<u8>,
    pub implementation: Vec<u8>,
}
impl Default for Opt {
    fn default() -> Self {
        Opt {
            include: Vec::new(),
            cxx_impl_annotations: None,
            gen_header: true,
            gen_implementation: true,
            allow_dot_includes: true,
            cfg_evaluator: Box::new(UnsupportedCfgEvaluator),
            doxygen: false,
        }
    }
}
pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode {
    let source = match read_to_string(path) {
        Ok(source) => source,
        Err(err) => format_err(path, "", err),
    };
    match generate_from_string(&source, opt) {
        Ok(out) => out,
        Err(err) => format_err(path, &source, err),
    }
}
fn read_to_string(path: &Path) -> Result<String> {
    let bytes = if path == Path::new("-") {
        fs::read_stdin()
    } else {
        fs::read(path)
    }?;
    match String::from_utf8(bytes) {
        Ok(string) => Ok(string),
        Err(err) => Err(Error::Utf8(path.to_owned(), err.utf8_error())),
    }
}
fn generate_from_string(source: &str, opt: &Opt) -> Result<GeneratedCode> {
    let mut source = source;
    if source.starts_with("#!") && !source.starts_with("#![") {
        let shebang_end = source.find('\n').unwrap_or(source.len());
        source = &source[shebang_end..];
    }
    let syntax: File = syn::parse_str(source)?;
    generate(syntax, opt)
}
pub(super) fn generate(syntax: File, opt: &Opt) -> Result<GeneratedCode> {
    if syntax.modules.is_empty() {
        return Err(Error::NoBridgeMod);
    }
    let ref mut apis = Vec::new();
    let ref mut errors = Errors::new();
    let ref mut cfg_errors = Set::new();
    for bridge in syntax.modules {
        let mut cfg = CfgExpr::Unconditional;
        attrs::parse(
            errors,
            bridge.attrs,
            attrs::Parser {
                cfg: Some(&mut cfg),
                ignore_unrecognized: true,
                ..Default::default()
            },
        );
        if cfg::eval(errors, cfg_errors, opt.cfg_evaluator.as_ref(), &cfg) {
            let ref namespace = bridge.namespace;
            let trusted = bridge.unsafety.is_some();
            apis.extend(syntax::parse_items(
                errors,
                bridge.content,
                trusted,
                namespace,
            ));
        }
    }
    cfg::strip(errors, cfg_errors, opt.cfg_evaluator.as_ref(), apis);
    errors.propagate()?;
    let ref types = Types::collect(errors, apis);
    check::precheck(errors, apis, opt);
    errors.propagate()?;
    let generator = check::Generator::Build;
    check::typecheck(errors, apis, types, generator);
    errors.propagate()?;
    let (mut header, mut implementation) = Default::default();
    if opt.gen_header {
        header = write::gen(apis, types, opt, true);
    }
    if opt.gen_implementation {
        implementation = write::gen(apis, types, opt, false);
    }
    Ok(GeneratedCode {
        header,
        implementation,
    })
}