alef 0.110.4

Opinionated polyglot binding generator for Rust libraries
Documentation
pub(super) fn emit(prefix: &str, out: &mut String) {
    out.push_str(&format!(
        r#"/// Errors reported by the verified downloadable-component manager. Use
/// `componentLastErrorCode` after catching one of these for the stable failure code --
/// "offline", "signature_invalid", "unsupported_host", and so on -- instead of matching on
/// `_last_error()`'s free-form message.
pub const ComponentError = error{{ComponentOperationFailed}};

/// The stable failure code of the most recent failing component operation on this thread
/// ("offline", "signature_invalid", "unsupported_host", ...), or null if the last operation
/// succeeded or the native layer did not prefix its message with one. The native layer
/// prefixes every component failure message with "<code>: " (see
/// alef_component_error_message in alef::backends::native_components); this splits that
/// prefix back out of `_last_error()` rather than exposing a second native accessor.
pub fn componentLastErrorCode() ?[]const u8 {{
    const message = _last_error() orelse return null;
    const separator = std.mem.indexOf(u8, message, ": ") orelse return null;
    return message[0..separator];
}}

/// Download, verify, dynamically load, and pin a configured component.
pub fn componentLoad(allocator: std.mem.Allocator, component: []const u8) (ComponentError || std.mem.Allocator.Error)!void {{
    const component_z = try allocator.dupeZ(u8, component);
    defer allocator.free(component_z);
    if (c.{prefix}_component_load(component_z.ptr) != 0) return ComponentError.ComponentOperationFailed;
}}

/// Download and verify one component, or all configured components when component is null.
/// The returned bytes are a JSON array of cache paths owned by the caller's allocator.
pub fn componentPrefetch(
    allocator: std.mem.Allocator,
    component: ?[]const u8,
) (ComponentError || std.mem.Allocator.Error)![]u8 {{
    const component_z = if (component) |value| try allocator.dupeZ(u8, value) else null;
    defer if (component_z) |value| allocator.free(value);
    const raw = c.{prefix}_component_prefetch(if (component_z) |value| value.ptr else null);
    return takeComponentString(allocator, raw);
}}

/// Return ready, cached, not_downloaded, bundled, or unsupported:<reason> for a configured
/// component. See `componentStatusCode` for the matching numeric code.
pub fn componentStatus(
    allocator: std.mem.Allocator,
    component: []const u8,
) (ComponentError || std.mem.Allocator.Error)![]u8 {{
    const component_z = try allocator.dupeZ(u8, component);
    defer allocator.free(component_z);
    return takeComponentString(allocator, c.{prefix}_component_status(component_z.ptr));
}}

/// The numeric counterpart to `componentStatus`, stable across releases: 0 ready, 1 cached,
/// 2 not_downloaded, 3 bundled, 4 unsupported.
pub fn componentStatusCode(
    allocator: std.mem.Allocator,
    component: []const u8,
) (ComponentError || std.mem.Allocator.Error)!i32 {{
    const component_z = try allocator.dupeZ(u8, component);
    defer allocator.free(component_z);
    const result = c.{prefix}_component_status_code(component_z.ptr);
    if (result < 0) return ComponentError.ComponentOperationFailed;
    return result;
}}

/// Return the content-addressed cache path for a configured component.
pub fn componentCachePath(
    allocator: std.mem.Allocator,
    component: []const u8,
) (ComponentError || std.mem.Allocator.Error)![]u8 {{
    const component_z = try allocator.dupeZ(u8, component);
    defer allocator.free(component_z);
    return takeComponentString(allocator, c.{prefix}_component_cache_path(component_z.ptr));
}}

fn takeComponentString(
    allocator: std.mem.Allocator,
    raw: [*c]u8,
) (ComponentError || std.mem.Allocator.Error)![]u8 {{
    if (raw == null) return ComponentError.ComponentOperationFailed;
    defer c.{prefix}_free_string(raw);
    return allocator.dupe(u8, std.mem.span(raw));
}}
"#,
    ));
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn emits_zig_wrappers_over_shared_c_manager() {
        let mut generated = String::new();
        emit("demo", &mut generated);
        assert!(generated.contains("pub fn componentLoad"));
        assert!(generated.contains("pub fn componentPrefetch"));
        assert!(generated.contains("c.demo_component_status(component_z.ptr)"));
        assert!(generated.contains("pub fn componentStatusCode"));
        assert!(generated.contains("c.demo_component_status_code(component_z.ptr)"));
        assert!(generated.contains("pub fn componentLastErrorCode"));
        assert!(generated.contains("c.demo_component_cache_path(component_z.ptr)"));
        assert!(generated.contains("defer c.demo_free_string(raw)"));
    }
}