apple-bindgen 0.3.0

bindgen helper for Apple frameworks
Documentation
use serde::Deserialize;
use std::collections::HashMap;
use thiserror::Error;

#[derive(Deserialize)]
pub struct ConfigMap {
    #[serde(flatten)]
    map: HashMap<String, FileConfig>,
}

impl ConfigMap {
    pub fn from_str(content: &str) -> Result<Self, ConfigError> {
        toml::from_str(&content).map_err(|e| ConfigError::TomlError(e))
    }

    pub fn from_file(path: impl AsRef<std::path::Path>) -> Result<Self, ConfigError> {
        let content = std::fs::read_to_string(path).map_err(|e| ConfigError::OpenError(e))?;
        Self::from_str(&content)
    }

    pub fn with_builtin_config() -> Self {
        let content = include_str!("../Bindgen.toml");
        Self::from_str(content).expect("Builtin config file is corrupted. This is bug.")
    }

    pub fn framework_config(&self, name: &str) -> Config {
        let mut deps = Vec::new();
        if self.map.contains_key(name) {
            deps.push(name);
        }

        let mut i = 0;
        while i < deps.len() {
            let dep = deps[i];
            let config = self
                .map
                .get(dep)
                .unwrap_or_else(|| panic!("wrong dependency name {dep}"));
            for dep in &config.deps {
                if deps.contains(&dep.as_str()) {
                    continue;
                }
                deps.push(dep.as_str());
            }
            i += 1;
        }
        deps.push("default");
        {
            let mut cloned = deps.clone();
            cloned.sort_unstable();
            cloned.dedup();
            assert_eq!(deps.len(), cloned.len());
        }

        deps.into_iter()
            .map(|dep| &self.map.get(dep).unwrap().config)
            .collect()
    }
}

impl<'a> FromIterator<&'a Config> for Config {
    fn from_iter<T: IntoIterator<Item = &'a Config>>(iter: T) -> Self {
        let mut opaque_types = Vec::new();
        let mut blocklist_items = Vec::new();
        let mut impl_debugs = Vec::new();
        let mut replacements = Vec::new();
        let mut layout_tests = false;
        for config in iter {
            opaque_types.extend(config.opaque_types.iter().cloned());
            blocklist_items.extend(config.blocklist_items.iter().cloned());
            impl_debugs.extend(config.impl_debugs.iter().cloned());
            replacements.extend(config.replacements.iter().cloned());
            layout_tests |= config.layout_tests;
        }
        Self {
            opaque_types,
            blocklist_items,
            impl_debugs,
            replacements,
            layout_tests,
        }
    }
}

#[derive(Deserialize, Default)]
pub struct FileConfig {
    #[serde(default)]
    deps: Vec<String>,
    #[serde(flatten)]
    config: Config,
}

#[derive(Deserialize, Default, Debug, Clone)]
pub struct Config {
    #[serde(default)]
    pub opaque_types: Vec<String>,
    #[serde(default)]
    pub blocklist_items: Vec<String>,
    #[serde(default)]
    pub impl_debugs: Vec<String>,
    #[serde(default)]
    pub replacements: Vec<String>,
    #[serde(default)]
    pub layout_tests: bool,
}

#[non_exhaustive]
#[derive(Debug, Error)]
pub enum ConfigError {
    #[error("toml parsing error {0}")]
    OpenError(std::io::Error),
    #[error("toml parsing error {0}")]
    TomlError(toml::de::Error),
}