alef 0.25.55

Opinionated polyglot binding generator for Rust libraries
Documentation
use super::{options, r_wrappers};
use crate::core::backend::GeneratedFile;
use crate::core::config::{ResolvedCrateConfig, resolve_output_dir};
use crate::core::hash::{self, CommentStyle};
use crate::core::ir::ApiSurface;
use std::path::PathBuf;

pub(super) fn generate_public_api(
    api: &ApiSurface,
    config: &ResolvedCrateConfig,
) -> anyhow::Result<Vec<GeneratedFile>> {
    let package_name = config.r_package_name();

    let r_wrapper_dir = if let Some(rust_out) = config.output_paths.get("r") {
        let rust_str = rust_out.to_string_lossy();
        let suffixes = ["src/rust/src/", "src/rust/src"];
        let base = suffixes
            .iter()
            .find_map(|s| rust_str.strip_suffix(s))
            .unwrap_or_else(|| rust_str.as_ref());
        format!("{base}R/")
    } else {
        "packages/r/R/".to_string()
    };
    let r_pkg_dir = r_wrapper_dir.trim_end_matches("R/").trim_end_matches("R");

    let mut files = Vec::new();

    let mut pkg_content = hash::header(CommentStyle::Hash);
    pkg_content.push('\n');
    pkg_content.push_str(&crate::backends::extendr::template_env::render(
        "r_use_dyn_lib.jinja",
        minijinja::context! { package_name => package_name },
    ));
    pkg_content.push_str("NULL\n");
    files.push(GeneratedFile {
        path: PathBuf::from(&r_wrapper_dir).join(format!("{package_name}.R")),
        content: pkg_content,
        generated_header: false,
    });

    let input_type_names = crate::codegen::conversions::input_type_names(api);
    let trait_bridge_fns = super::trait_bridge_wrappers::collect_trait_bridge_functions(config);
    let r_exclude_functions: ahash::AHashSet<String> = config
        .r
        .as_ref()
        .map(|c| c.exclude_functions.iter().cloned().collect())
        .unwrap_or_default();
    let wrappers_content = r_wrappers::gen_extendr_wrappers_r(
        api,
        &package_name,
        &input_type_names,
        &trait_bridge_fns,
        &r_exclude_functions,
        &config.trait_bridges,
    );
    files.push(GeneratedFile {
        path: PathBuf::from(&r_wrapper_dir).join("extendr-wrappers.R"),
        content: wrappers_content,
        generated_header: false,
    });

    let namespace_content = r_wrappers::gen_namespace(
        api,
        &package_name,
        &trait_bridge_fns,
        &r_exclude_functions,
        &config.trait_bridges,
    );
    files.push(GeneratedFile {
        path: PathBuf::from(r_pkg_dir).join("NAMESPACE"),
        content: namespace_content,
        generated_header: false,
    });

    if let Some(opts_type) = options::find_r_options_type(api, config) {
        files.push(GeneratedFile {
            path: PathBuf::from(&r_wrapper_dir).join("options.R"),
            content: options::gen_conversion_options_r(opts_type),
            generated_header: true,
        });
    }

    if let Some(opts_type) = options::find_r_options_type(api, config) {
        let core_import = config.core_import_name();
        let options_rs = options::gen_options_rs(api, opts_type, &core_import);
        let rust_output_path =
            resolve_output_dir(config.output_paths.get("r"), &config.name, "packages/r/src/rust/src");
        files.push(GeneratedFile {
            path: PathBuf::from(&rust_output_path).join("options.rs"),
            content: options_rs,
            generated_header: true,
        });
    }

    Ok(files)
}