alef 0.69.0

Opinionated polyglot binding generator for Rust libraries
Documentation
//! Coverage for the umbrella-header decision, built around the two shapes the same header
//! genuinely takes on disk: swift-bridge's raw output, and the clang-format-normalized bytes
//! that ship once a formatting pass has seen it. [`FORMATTED_UMBRELLA_HEADER`] is the literal
//! `clang-format` output for [`RAW_CORE_H`] + [`RAW_CRATE_H`] — reordered `#include`s,
//! expanded struct bodies, moved pointer stars — so the equivalence assertions below are
//! measured against a real formatter's rewrite rather than a hand-guessed one.

use super::*;

const BINDING_CRATE: &str = "demo-swift";

/// swift-bridge's own `SwiftBridgeCore.h`, verbatim in style: one line per typedef, `stdint`
/// before `stdbool`, `uint8_t* const` rather than `uint8_t *const`.
const RAW_CORE_H: &str = "#include <stdint.h>\n\
                          #include <stdbool.h>\n\
                          typedef struct RustStr { uint8_t* const start; uintptr_t len; } RustStr;\n\
                          typedef struct __private__FfiSlice { void* const start; uintptr_t len; } __private__FfiSlice;\n\
                          void* __swift_bridge__null_pointer(void);\n\
                          \n\
                          typedef struct __private__OptionU8 { uint8_t val; bool is_some; } __private__OptionU8;\n\
                          typedef struct __private__OptionBool { bool val; bool is_some; } __private__OptionBool;\n";

const RAW_CRATE_H: &str = "void* __swift_bridge__$Vec_Widget$new();\n\
                           void __swift_bridge__$Vec_Widget$_free(void* const vec);\n\
                           __private__OptionU8 __swift_bridge__$Vec_Widget$pop(void* const vec);\n\
                           struct RustStr __swift_bridge__$Widget$label(void* self);\n\
                           __private__OptionBool __swift_bridge__$Widget$enabled(void* self);\n";

/// The same header after `clang-format` and after `finalize_hashes` stamped it — i.e. exactly
/// what a consumer has committed.
const FORMATTED_UMBRELLA_HEADER: &str = r#"#ifndef RUST_BRIDGE_C_H
#define RUST_BRIDGE_C_H

// Auto-generated by alef — do not edit by hand.
// alef:hash:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
// Concatenates SwiftBridgeCore.h and demo-swift.h produced by
// `cargo build -p demo-swift` via swift_bridge_build.

#include <stdbool.h>
#include <stdint.h>
typedef struct RustStr {
  uint8_t *const start;
  uintptr_t len;
} RustStr;
typedef struct __private__FfiSlice {
  void *const start;
  uintptr_t len;
} __private__FfiSlice;
void *__swift_bridge__null_pointer(void);

typedef struct __private__OptionU8 {
  uint8_t val;
  bool is_some;
} __private__OptionU8;
typedef struct __private__OptionBool {
  bool val;
  bool is_some;
} __private__OptionBool;

void *__swift_bridge__$Vec_Widget$new();
void __swift_bridge__$Vec_Widget$_free(void *const vec);
__private__OptionU8 __swift_bridge__$Vec_Widget$pop(void *const vec);
struct RustStr __swift_bridge__$Widget$label(void *self);
__private__OptionBool __swift_bridge__$Widget$enabled(void *self);
#endif /* RUST_BRIDGE_C_H */
"#;

fn raw_umbrella_header() -> String {
    assemble(BINDING_CRATE, RAW_CORE_H, RAW_CRATE_H)
}

#[test]
fn raw_and_formatted_umbrella_headers_declare_the_same_c() {
    assert!(
        declares_the_same_c(&raw_umbrella_header(), FORMATTED_UMBRELLA_HEADER),
        "clang-format's rewrite of the same swift-bridge output must not read as a change"
    );
}

#[test]
fn a_dropped_declaration_is_not_the_same_c() {
    let truncated = FORMATTED_UMBRELLA_HEADER.replace(
        "__private__OptionBool __swift_bridge__$Widget$enabled(void *self);\n",
        "",
    );
    assert_ne!(
        truncated, FORMATTED_UMBRELLA_HEADER,
        "sanity: the replace must have removed a line"
    );
    assert!(
        !declares_the_same_c(&raw_umbrella_header(), &truncated),
        "a header missing a __swift_bridge__$ declaration must not compare equal"
    );
}

#[test]
fn a_dropped_core_typedef_is_not_the_same_c() {
    let without_core = FORMATTED_UMBRELLA_HEADER.replace(
        "typedef struct __private__FfiSlice {\n  void *const start;\n  uintptr_t len;\n} __private__FfiSlice;\n",
        "",
    );
    assert_ne!(
        without_core, FORMATTED_UMBRELLA_HEADER,
        "sanity: the replace must have removed the typedef"
    );
    assert!(
        !declares_the_same_c(&raw_umbrella_header(), &without_core),
        "a header missing a core typedef must not compare equal"
    );
}

/// Order is ignored *between* declarations (clang-format sorts `#include`s) but not *within*
/// one, so two headers cannot pass as equal merely by using the same tokens.
#[test]
fn a_swapped_field_type_is_not_the_same_c() {
    let swapped = FORMATTED_UMBRELLA_HEADER.replace(
        "typedef struct __private__OptionU8 {\n  uint8_t val;\n  bool is_some;\n}",
        "typedef struct __private__OptionU8 {\n  bool val;\n  uint8_t is_some;\n}",
    );
    assert_ne!(
        swapped, FORMATTED_UMBRELLA_HEADER,
        "sanity: the replace must have swapped the field types"
    );
    assert!(
        !declares_the_same_c(&raw_umbrella_header(), &swapped),
        "reusing the same tokens in a different order within a declaration is a real change"
    );
}

#[test]
fn a_complete_assembly_has_no_undefined_core_types() {
    assert_eq!(undefined_core_types(&raw_umbrella_header()), Vec::<String>::new());
    assert_eq!(undefined_core_types(FORMATTED_UMBRELLA_HEADER), Vec::<String>::new());
}

/// The failure this whole module exists for: `SwiftBridgeCore.h` present on disk but
/// degenerate. Every exists-check still passes and the concatenation still looks like a
/// header — it just cannot compile, because nothing defines the types its declarations use.
#[test]
fn an_empty_core_header_leaves_core_types_undefined() {
    let partial = assemble(BINDING_CRATE, "", RAW_CRATE_H);
    assert!(
        is_populated(&partial),
        "sanity: the partial header still carries real declarations"
    );
    assert_eq!(
        undefined_core_types(&partial),
        vec![
            "RustStr".to_string(),
            "__private__OptionBool".to_string(),
            "__private__OptionU8".to_string(),
        ]
    );
}

#[test]
fn a_binding_crate_using_no_core_types_is_not_flagged() {
    let plain = assemble(BINDING_CRATE, "", "void __swift_bridge__$Widget$_free(void* self);\n");
    assert_eq!(
        undefined_core_types(&plain),
        Vec::<String>::new(),
        "core types no declaration references must not be demanded"
    );
}

/// The regression test for the shipped incident: a committed, formatted header and a fresh
/// raw assembly that declare the same C must leave the committed bytes alone. Rewriting them
/// makes which of the two ships a function of whether a formatting pass happened to run
/// before the hash stamp, not of any input.
#[test]
fn resolve_fresh_preserves_a_committed_header_declaring_the_same_c() {
    let resolved = resolve_fresh(BINDING_CRATE, RAW_CORE_H, RAW_CRATE_H, Some(FORMATTED_UMBRELLA_HEADER))
        .expect("a complete assembly must resolve");
    assert_eq!(
        resolved, FORMATTED_UMBRELLA_HEADER,
        "the committed header must survive byte-for-byte when its C is unchanged"
    );
}

#[test]
fn resolve_fresh_rewrites_when_the_declarations_change() {
    let extended = format!("{RAW_CRATE_H}void __swift_bridge__$Widget$reset(void* self);\n");
    let resolved = resolve_fresh(BINDING_CRATE, RAW_CORE_H, &extended, Some(FORMATTED_UMBRELLA_HEADER))
        .expect("a complete assembly must resolve");
    assert!(
        resolved.contains("__swift_bridge__$Widget$reset"),
        "a genuinely new declaration must reach the header; got:\n{resolved}"
    );
}

#[test]
fn resolve_fresh_keeps_the_committed_header_when_the_assembly_is_partial() {
    let resolved = resolve_fresh(BINDING_CRATE, "", RAW_CRATE_H, Some(FORMATTED_UMBRELLA_HEADER))
        .expect("a partial assembly with a committed fallback must not fail the run");
    assert_eq!(
        resolved, FORMATTED_UMBRELLA_HEADER,
        "a partial assembly must never replace a populated committed header"
    );
}

#[test]
fn resolve_fresh_refuses_a_partial_assembly_with_no_committed_fallback() {
    let error = resolve_fresh(BINDING_CRATE, "", RAW_CRATE_H, None)
        .expect_err("a partial assembly with nothing to fall back on must fail loudly");
    let message = format!("{error:#}");
    assert!(
        message.contains("refusing to write an incomplete RustBridgeC.h"),
        "the failure must name what it refused; got: {message}"
    );
    assert!(
        message.contains("RustStr"),
        "the failure must name the undefined type so the cause is actionable; got: {message}"
    );
}

/// A placeholder header is not a fallback: it carries no `__swift_bridge__$` declaration, so
/// a partial assembly must still fail rather than quietly resolving to it.
#[test]
fn resolve_fresh_does_not_treat_a_placeholder_as_a_committed_fallback() {
    let placeholder = "#ifndef RUST_BRIDGE_C_H\n#define RUST_BRIDGE_C_H\ntypedef struct RustStr { uint8_t* const start; uintptr_t len; } RustStr;\n#endif\n";
    assert!(
        !is_populated(placeholder),
        "sanity: the placeholder must not look populated"
    );
    resolve_fresh(BINDING_CRATE, "", RAW_CRATE_H, Some(placeholder))
        .expect_err("a placeholder must not absorb a partial assembly");
}

/// swift-bridge emits some core types as a plain tag definition (`struct NAME { ... };`)
/// rather than the `typedef struct NAME { ... } NAME;` form the `__private__Option*` family
/// uses. Both count as defining the type; a checker that only understood the typedef form
/// would report every plain-tag type as missing on a header that is in fact complete.
#[test]
fn a_plain_struct_tag_definition_counts_as_defining_the_type() {
    let core = "struct __private__ResultPtrAndPtr {\n  bool is_ok;\n  void *ok_or_err;\n};\n";
    let crate_h = "struct __private__ResultPtrAndPtr __swift_bridge__$Widget$parse(void *self);\n";
    assert_eq!(
        undefined_core_types(&assemble(BINDING_CRATE, core, crate_h)),
        Vec::<String>::new(),
        "a plain `struct NAME {{ .. }};` definition must satisfy references to NAME"
    );
    assert_eq!(
        undefined_core_types(&assemble(BINDING_CRATE, "", crate_h)),
        vec!["__private__ResultPtrAndPtr".to_string()],
        "without the definition the same reference must be reported"
    );
}