alef 0.71.0

Opinionated polyglot binding generator for Rust libraries
Documentation
use super::{floor_against, requirement_floor};

/// A rendered JNI-shaped manifest carrying the emitter's stale `base64` literal, plus the
/// surrounding comments and tables a real one has, so the preservation assertions below
/// are made against something the emitters actually produce rather than a bare table.
const RENDERED: &str = r#"# Generated by alef. Do not edit by hand.

[package]
name = "demo-lib-jni"
version.workspace = true

# This crate deliberately does not use `[lints]` / `workspace = true`. ~keep
[lints.clippy]
dbg_macro = "deny"

[dependencies]
async-trait = "0.1"
base64 = "0.22"
demo-lib = { version = "1.0.0", path = "../demo-lib" }
serde_json = "1"
"#;

fn dependency_line(manifest: &str, name: &str) -> String {
    manifest
        .lines()
        .find(|line| line.starts_with(&format!("{name} ")))
        .unwrap_or_else(|| panic!("`{name}` must appear in:\n{manifest}"))
        .to_string()
}

/// The reported defect: the consumer's committed manifest is on `base64 = "0.23"` and the
/// emitter's literal is `"0.22"`, so every regeneration hands back a downgrade that has to
/// be reverted by hand. The emitted requirement must come out at the committed one.
#[test]
fn floor_raises_an_emitted_requirement_to_the_higher_committed_one() {
    let committed = "[dependencies]\nbase64 = \"0.23\"\n";

    let floored = floor_against(RENDERED, committed);

    assert_eq!(dependency_line(&floored, "base64"), "base64 = \"0.23\"");
}

/// The invariant, stated as its own test rather than inferred from the one above: the
/// floor is one-directional. A consumer sitting *behind* the emitter must still be moved
/// forward, which is the whole reason alef writes a version at all.
#[test]
fn floor_never_lowers_an_emitted_requirement_to_a_lower_committed_one() {
    let rendered = "[dependencies]\nbase64 = \"0.23\"\n";
    let committed = "[dependencies]\nbase64 = \"0.22\"\n";

    let floored = floor_against(rendered, committed);

    assert_eq!(dependency_line(&floored, "base64"), "base64 = \"0.23\"");
}

/// Convergence. The floor makes rendered output depend on disk state, so a second run must
/// be a no-op or `alef verify` would report a manifest permanently stale against itself.
#[test]
fn floor_is_a_no_op_once_the_committed_manifest_already_matches() {
    let committed = "[dependencies]\nbase64 = \"0.23\"\n";
    let once = floor_against(RENDERED, committed);

    let twice = floor_against(&once, &once);

    assert_eq!(twice, once, "a second pass over its own output must change nothing");
}

/// The anti-vacuity control for every preservation assertion here: when nothing needs
/// raising the input must come back byte-for-byte, not merely equivalent. A `toml_edit`
/// round-trip that reordered keys or dropped the `~keep` rationale would be invisible to a
/// `contains` check but would rewrite the consumer's whole manifest on every run.
#[test]
fn floor_returns_the_rendered_manifest_verbatim_when_nothing_is_behind() {
    let committed = "[dependencies]\nbase64 = \"0.22\"\n";

    assert_eq!(floor_against(RENDERED, committed), RENDERED);
}

/// Raising one requirement must not cost the manifest its comments, its blank lines or its
/// key order -- the `~keep` lints rationale is emitted as a comment and would be the first
/// casualty of a naive parse-and-reserialise.
#[test]
fn floor_preserves_comments_and_layout_around_the_requirement_it_raises() {
    let committed = "[dependencies]\nbase64 = \"0.23\"\n";

    let floored = floor_against(RENDERED, committed);

    assert!(
        floored.contains("# This crate deliberately does not use `[lints]` / `workspace = true`. ~keep"),
        "the marked rationale comment must survive a raise:\n{floored}"
    );
    assert!(
        floored.contains("# Generated by alef. Do not edit by hand."),
        "the header comment must survive a raise:\n{floored}"
    );
    assert_eq!(dependency_line(&floored, "async-trait"), "async-trait = \"0.1\"");
    assert_eq!(dependency_line(&floored, "serde_json"), "serde_json = \"1\"");
}

/// A table-form entry carries the emitter's feature selection, which encodes what the
/// generated code needs and is not the consumer's to override. Only the version moves.
#[test]
fn floor_raises_a_table_entry_version_and_leaves_its_features_alone() {
    let rendered = "[dependencies]\ntokio = { version = \"1.0\", features = [\"sync\"] }\n";
    let committed = "[dependencies]\ntokio = { version = \"1.40\", features = [\"full\"] }\n";

    let floored = floor_against(rendered, committed);

    assert_eq!(
        dependency_line(&floored, "tokio"),
        "tokio = { version = \"1.40\", features = [\"sync\"] }"
    );
}

/// A committed entry that inherits from the workspace declares no version of its own, so
/// there is nothing to order against and the emitter's literal stands. Reading `0.0.0`
/// into the absence would make every such entry look behind.
#[test]
fn floor_ignores_a_committed_entry_that_declares_no_version() {
    let committed = "[dependencies]\nbase64.workspace = true\n";

    assert_eq!(floor_against(RENDERED, committed), RENDERED);
}

/// A requirement with no lower bound is not ordered against one that has a lower bound.
/// Treating `*` as `0.0.0` would let every emitted literal "raise" past it; treating it as
/// unbounded above would let it swallow every emitted literal. Neither is a ruling, so the
/// floor declines to make one.
#[test]
fn floor_declines_to_rule_on_a_requirement_with_no_lower_bound() {
    let committed = "[dependencies]\nbase64 = \"*\"\n";

    assert_eq!(floor_against(RENDERED, committed), RENDERED);
}

/// A manifest alef cannot parse is one alef must not rewrite from.
#[test]
fn floor_leaves_the_rendered_manifest_alone_when_the_committed_one_is_not_valid_toml() {
    assert_eq!(floor_against(RENDERED, "[dependencies\nbase64 = "), RENDERED);
}

/// The wasm crate declares `getrandom` only under `cfg(target_arch = "wasm32")`, so a
/// floor that walked the document root alone would leave exactly those entries exposed.
#[test]
fn floor_raises_requirements_nested_under_a_target_cfg_table() {
    let rendered = "[target.'cfg(target_arch = \"wasm32\")'.dependencies]\ngetrandom = \"0.2\"\n";
    let committed = "[target.'cfg(target_arch = \"wasm32\")'.dependencies]\ngetrandom = \"0.3\"\n";

    let floored = floor_against(rendered, committed);

    assert_eq!(dependency_line(&floored, "getrandom"), "getrandom = \"0.3\"");
}

/// `dev-dependencies` and `build-dependencies` are as regenerated as `dependencies`; the
/// FFI manifest's `cbindgen` build-dep and `tempfile` dev-dep live in exactly those.
#[test]
fn floor_covers_dev_and_build_dependency_tables() {
    let rendered = "[build-dependencies]\ncbindgen = \"0.28\"\n\n[dev-dependencies]\ntempfile = \"3\"\n";
    let committed = "[build-dependencies]\ncbindgen = \"0.29\"\n\n[dev-dependencies]\ntempfile = \"3.10\"\n";

    let floored = floor_against(rendered, committed);

    assert_eq!(dependency_line(&floored, "cbindgen"), "cbindgen = \"0.29\"");
    assert_eq!(dependency_line(&floored, "tempfile"), "tempfile = \"3.10\"");
}

/// A dependency the consumer added by hand is not the floor's business -- it belongs to
/// the ownership guard and to `[crates].extra_dependencies`. The floor only ever moves an
/// entry the emitter itself wrote.
#[test]
fn floor_does_not_import_a_dependency_the_emitter_did_not_declare() {
    let committed = "[dependencies]\nbase64 = \"0.22\"\nuuid = \"1\"\n";

    let floored = floor_against(RENDERED, committed);

    assert!(
        !floored.contains("uuid"),
        "an unemitted dependency must not appear:\n{floored}"
    );
}

#[test]
fn requirement_floor_reads_the_lower_bound_of_each_requirement_form() {
    let cases = [
        ("0.22", Some("0.22.0")),
        ("0.23", Some("0.23.0")),
        ("1", Some("1.0.0")),
        ("1.2.3", Some("1.2.3")),
        ("^1.4", Some("1.4.0")),
        ("~0.9.1", Some("0.9.1")),
        ("=2.0.4", Some("2.0.4")),
        (">=0.9, <0.9.128", Some("0.9.0")),
        ("*", None),
        ("<2", None),
        ("not a requirement", None),
    ];

    for (requirement, expected) in cases {
        let actual = requirement_floor(requirement).map(|version| version.to_string());
        assert_eq!(
            actual.as_deref(),
            expected,
            "lower bound of `{requirement}` must be {expected:?}"
        );
    }
}

/// `0.22` and `0.23` are disjoint caret ranges, so neither contains the other and only
/// their lower bounds order them. If this ever stops holding, the central comparison in
/// `raise_table` stops meaning what its name says.
#[test]
fn requirement_floor_orders_two_disjoint_caret_ranges() {
    let behind = requirement_floor("0.22").expect("0.22 is bounded below");
    let ahead = requirement_floor("0.23").expect("0.23 is bounded below");

    assert!(ahead > behind, "0.23 must order above 0.22");
}