```zig title="Zig" const std = @import("std"); const kreuzberg = @import("kreuzberg"); // VTable struct for DocumentExtractor; mirrors KreuzbergDocumentExtractorVTable. const DocumentExtractorVTable = extern struct { name_fn: ?*const fn (user_data: ?*anyopaque, out_name: ?*?[*c]u8) callconv(.C) void, version_fn: ?*const fn (user_data: ?*anyopaque, out_version: ?*?[*c]u8) callconv(.C) void, initialize_fn: ?*const fn (user_data: ?*anyopaque, out_error: ?*?[*c]u8) callconv(.C) i32, shutdown_fn: ?*const fn (user_data: ?*anyopaque, out_error: ?*?[*c]u8) callconv(.C) i32, extract_bytes: ?*const fn ( user_data: ?*anyopaque, content: [*c]const u8, content_len: usize, mime_type: [*c]const u8, config: [*c]const u8, out_result: ?*?[*c]u8, out_error: ?*?[*c]u8, ) callconv(.C) i32, extract_file: ?*const fn ( user_data: ?*anyopaque, path: [*c]const u8, mime_type: [*c]const u8, config: [*c]const u8, out_result: ?*?[*c]u8, out_error: ?*?[*c]u8, ) callconv(.C) i32, supported_mime_types: ?*const fn (user_data: ?*anyopaque, out_result: ?*?[*c]u8) callconv(.C) i32, priority: ?*const fn (user_data: ?*anyopaque) callconv(.C) i32, can_handle: ?*const fn ( user_data: ?*anyopaque, path: [*c]const u8, mime_type: [*c]const u8, ) callconv(.C) i32, as_sync_extractor: ?*const fn (user_data: ?*anyopaque) callconv(.C) i32, free_user_data: ?*const fn (user_data: ?*anyopaque) callconv(.C) void, }; extern "kreuzberg_ffi" fn kreuzberg_register_document_extractor( name: [*c]const u8, vtable: DocumentExtractorVTable, user_data: ?*anyopaque, out_error: ?*?[*c]u8, ) i32; extern "kreuzberg_ffi" fn kreuzberg_unregister_document_extractor( name: [*c]const u8, out_error: ?*?[*c]u8, ) i32; extern "kreuzberg_ffi" fn kreuzberg_free_string(ptr: [*c]u8) void; // Implement callback functions for the extractor. fn extract_bytes_fn( user_data: ?*anyopaque, content: [*c]const u8, content_len: usize, mime_type: [*c]const u8, config: [*c]const u8, out_result: ?*?[*c]u8, out_error: ?*?[*c]u8, ) callconv(.C) i32 { _ = user_data; _ = content; _ = content_len; _ = config; const mime_str = std.mem.sliceTo(mime_type, 0); if (std.mem.eql(u8, mime_str, "application/json")) { const result = "{\"content\": \"Extracted from JSON\"}"; const result_cstr = std.heap.c_allocator.allocSentinel(u8, result.len, 0) catch return 1; @memcpy(result_cstr[0..result.len], result); if (out_result) |ptr| ptr.* = result_cstr.ptr; return 0; } if (out_error) |ptr| { const err_msg = "Unsupported MIME type"; const err_cstr = std.heap.c_allocator.allocSentinel(u8, err_msg.len, 0) catch return 1; @memcpy(err_cstr[0..err_msg.len], err_msg); ptr.* = err_cstr.ptr; } return 1; } fn supported_mime_types_fn(user_data: ?*anyopaque, out_result: ?*?[*c]u8) callconv(.C) i32 { _ = user_data; const mime_types = "[\"application/json\"]"; const cstr = std.heap.c_allocator.allocSentinel(u8, mime_types.len, 0) catch return 1; @memcpy(cstr[0..mime_types.len], mime_types); if (out_result) |ptr| ptr.* = cstr.ptr; return 0; } fn name_fn(user_data: ?*anyopaque, out_name: ?*?[*c]u8) callconv(.C) void { _ = user_data; const name = "zig-json-extractor"; if (std.heap.c_allocator.allocSentinel(u8, name.len, 0)) |cstr| { @memcpy(cstr[0..name.len], name); if (out_name) |ptr| ptr.* = cstr.ptr; } } fn version_fn(user_data: ?*anyopaque, out_version: ?*?[*c]u8) callconv(.C) void { _ = user_data; const version = "0.1.0"; if (std.heap.c_allocator.allocSentinel(u8, version.len, 0)) |cstr| { @memcpy(cstr[0..version.len], version); if (out_version) |ptr| ptr.* = cstr.ptr; } } fn initialize_fn(user_data: ?*anyopaque, out_error: ?*?[*c]u8) callconv(.C) i32 { _ = user_data; _ = out_error; return 0; // Success } fn shutdown_fn(user_data: ?*anyopaque, out_error: ?*?[*c]u8) callconv(.C) i32 { _ = user_data; _ = out_error; return 0; // Success } fn priority_fn(user_data: ?*anyopaque) callconv(.C) i32 { _ = user_data; return 50; // Default priority } pub fn main() !void { var out_error: ?[*c]u8 = null; defer if (out_error) |ptr| kreuzberg_free_string(ptr); // Build the vtable. const vtable = DocumentExtractorVTable{ .name_fn = name_fn, .version_fn = version_fn, .initialize_fn = initialize_fn, .shutdown_fn = shutdown_fn, .extract_bytes = extract_bytes_fn, .extract_file = null, .supported_mime_types = supported_mime_types_fn, .priority = priority_fn, .can_handle = null, .as_sync_extractor = null, .free_user_data = null, }; // Register the extractor with null user_data (no state). const register_rc = kreuzberg_register_document_extractor( "zig-json-extractor", vtable, null, &out_error, ); if (register_rc != 0) { const stdout = std.io.getStdOut().writer(); if (out_error) |err_ptr| { const err_msg = std.mem.sliceTo(err_ptr, 0); try stdout.print("Registration failed: {s}\n", .{err_msg}); } return; } const stdout = std.io.getStdOut().writer(); try stdout.print("Successfully registered zig-json-extractor\n", .{}); // Unregister the extractor when done. out_error = null; const unregister_rc = kreuzberg_unregister_document_extractor("zig-json-extractor", &out_error); if (unregister_rc == 0) { try stdout.print("Successfully unregistered zig-json-extractor\n", .{}); } } ```