alef 0.25.37

Opinionated polyglot binding generator for Rust libraries
Documentation
use crate::core::hash::{self, CommentStyle};

/// Strip trailing whitespace from every line and ensure the file ends with a single newline.
pub(super) fn strip_trailing_whitespace(content: &str) -> String {
    let mut result: String = content
        .lines()
        .map(|line| line.trim_end())
        .collect::<Vec<_>>()
        .join("\n");
    if !result.ends_with('\n') {
        result.push('\n');
    }
    result
}

/// Generate C# file header with hash and nullable-enable pragma.
pub(super) fn csharp_file_header() -> String {
    let mut out = hash::header(CommentStyle::DoubleSlash);
    out.push_str("#nullable enable\n\n");
    out
}

/// Generate Directory.Build.props with Nullable=enable and LangVersion=latest.
/// This is auto-generated (overwritten on each build) so it doesn't require user maintenance.
pub(super) fn gen_directory_build_props() -> String {
    "<!-- auto-generated by alef (generate_bindings) -->\n\
<Project>\n  \
<PropertyGroup>\n    \
<Nullable>enable</Nullable>\n    \
<LangVersion>latest</LangVersion>\n    \
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>\n  \
</PropertyGroup>\n\
</Project>\n"
        .to_string()
}

/// Delete `IVisitor.cs` and `VisitorCallbacks.cs` when visitor_callbacks is enabled but the
/// modern configured bridge / `TraitBridges.cs` path supersedes them.
/// These files are no longer emitted by `gen_visitor_files()` but may exist on disk from older
/// generator runs.
pub(super) fn delete_superseded_visitor_files(base_path: &std::path::Path) -> anyhow::Result<()> {
    let superseded = ["IVisitor.cs", "VisitorCallbacks.cs"];
    for filename in superseded {
        let path = base_path.join(filename);
        if path.exists() {
            std::fs::remove_file(&path)
                .map_err(|e| anyhow::anyhow!("Failed to delete superseded visitor file {}: {}", path.display(), e))?;
        }
    }
    Ok(())
}

/// Delete stale visitor-related files when visitor_callbacks is disabled.
/// When visitor_callbacks transitions from true → false, these files remain on disk
/// and cause CS8632 warnings (nullable context not enabled in these files).
pub(super) fn delete_stale_visitor_files(
    base_path: &std::path::Path,
    config: &crate::core::config::ResolvedCrateConfig,
) -> anyhow::Result<()> {
    let mut stale_files = vec!["IVisitor.cs".to_string(), "VisitorCallbacks.cs".to_string()];
    stale_files.extend(config.trait_bridges.iter().filter_map(|bridge| {
        bridge
            .context_type
            .as_deref()
            .map(|name| format!("{}.cs", crate::codegen::naming::csharp_type_name(name)))
    }));
    stale_files.extend(config.trait_bridges.iter().filter_map(|bridge| {
        bridge
            .result_type
            .as_deref()
            .map(|name| format!("{}.cs", crate::codegen::naming::csharp_type_name(name)))
    }));

    for filename in stale_files {
        let path = base_path.join(filename);
        if path.exists() {
            std::fs::remove_file(&path)
                .map_err(|e| anyhow::anyhow!("Failed to delete stale visitor file {}: {}", path.display(), e))?;
        }
    }

    Ok(())
}