boltffi_cli 0.27.2

CLI for BoltFFI - generate Swift, Kotlin, and WASM bindings from Rust
use std::collections::HashMap;
use std::path::PathBuf;

use serde::{Deserialize, Deserializer, Serialize};

use crate::config::{DebugSymbolsConfig, ErrorStyle, TypeMapping};
use crate::target::{Architecture, Platform};

#[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct SwiftConfig {
    pub module_name: Option<String>,
    pub output: Option<PathBuf>,
    pub ffi_module_name: Option<String>,
    pub tools_version: Option<String>,
    #[serde(default)]
    pub error_style: ErrorStyle,
    #[serde(default)]
    pub type_mappings: HashMap<String, TypeMapping>,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct AppleConfig {
    #[serde(default = "default_true")]
    pub enabled: bool,
    #[serde(default = "default_apple_output")]
    pub output: PathBuf,
    #[serde(default = "default_apple_deployment_target")]
    pub deployment_target: String,
    #[serde(default)]
    pub include_macos: bool,
    #[serde(
        default,
        deserialize_with = "AppleConfig::deserialize_ios_architectures"
    )]
    pub ios_architectures: Option<Vec<Architecture>>,
    #[serde(
        default,
        deserialize_with = "AppleConfig::deserialize_simulator_architectures"
    )]
    pub simulator_architectures: Option<Vec<Architecture>>,
    #[serde(
        default,
        deserialize_with = "AppleConfig::deserialize_macos_architectures"
    )]
    pub macos_architectures: Option<Vec<Architecture>>,
    #[serde(default)]
    pub swift: SwiftConfig,
    #[serde(default)]
    pub xcframework: XcframeworkConfig,
    #[serde(default)]
    pub spm: SpmConfig,
    #[serde(default)]
    pub debug_symbols: DebugSymbolsConfig,
}

impl Default for AppleConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            output: default_apple_output(),
            deployment_target: default_apple_deployment_target(),
            include_macos: false,
            ios_architectures: None,
            simulator_architectures: None,
            macos_architectures: None,
            swift: SwiftConfig::default(),
            xcframework: XcframeworkConfig::default(),
            spm: SpmConfig::default(),
            debug_symbols: DebugSymbolsConfig::default(),
        }
    }
}

impl AppleConfig {
    fn deserialize_ios_architectures<'de, D>(
        deserializer: D,
    ) -> Result<Option<Vec<Architecture>>, D::Error>
    where
        D: Deserializer<'de>,
    {
        Self::deserialize_architectures(
            deserializer,
            Platform::Ios,
            "targets.apple.ios_architectures",
        )
    }

    fn deserialize_simulator_architectures<'de, D>(
        deserializer: D,
    ) -> Result<Option<Vec<Architecture>>, D::Error>
    where
        D: Deserializer<'de>,
    {
        Self::deserialize_architectures(
            deserializer,
            Platform::IosSimulator,
            "targets.apple.simulator_architectures",
        )
    }

    fn deserialize_macos_architectures<'de, D>(
        deserializer: D,
    ) -> Result<Option<Vec<Architecture>>, D::Error>
    where
        D: Deserializer<'de>,
    {
        Self::deserialize_architectures(
            deserializer,
            Platform::MacOs,
            "targets.apple.macos_architectures",
        )
    }

    fn deserialize_architectures<'de, D>(
        deserializer: D,
        platform: Platform,
        field: &'static str,
    ) -> Result<Option<Vec<Architecture>>, D::Error>
    where
        D: Deserializer<'de>,
    {
        let architectures = Option::<Vec<Architecture>>::deserialize(deserializer)?;
        if let Some(architectures) = architectures.as_deref() {
            Self::ensure_supported_architectures(platform, field, architectures)?;
        }
        Ok(architectures)
    }

    fn ensure_supported_architectures<E>(
        platform: Platform,
        field: &'static str,
        architectures: &[Architecture],
    ) -> Result<(), E>
    where
        E: serde::de::Error,
    {
        architectures
            .iter()
            .find(|architecture| !platform.architectures().contains(architecture))
            .map_or(Ok(()), |architecture| {
                Err(E::custom(format!(
                    "{field} does not support {}",
                    architecture.canonical_name()
                )))
            })
    }
}

#[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct XcframeworkConfig {
    pub output: Option<PathBuf>,
    pub name: Option<String>,
}

#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq, Default)]
#[serde(rename_all = "lowercase")]
pub enum SpmDistribution {
    #[default]
    Local,
    Remote,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct SpmConfig {
    pub output: Option<PathBuf>,
    #[serde(default)]
    pub distribution: SpmDistribution,
    pub repo_url: Option<String>,
    #[serde(default)]
    pub layout: SpmLayout,
    pub package_name: Option<String>,
    pub wrapper_sources: Option<PathBuf>,
    #[serde(default)]
    pub skip_package_swift: bool,
}

impl Default for SpmConfig {
    fn default() -> Self {
        Self {
            output: None,
            distribution: SpmDistribution::Local,
            repo_url: None,
            layout: SpmLayout::default(),
            package_name: None,
            wrapper_sources: None,
            skip_package_swift: false,
        }
    }
}

#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq, Default)]
#[serde(rename_all = "kebab-case")]
pub enum SpmLayout {
    Bundled,
    Split,
    #[default]
    FfiOnly,
}

fn default_apple_output() -> PathBuf {
    PathBuf::from("dist/apple")
}

fn default_apple_deployment_target() -> String {
    "16.0".to_string()
}

fn default_true() -> bool {
    true
}