Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Functions cannot be `async`.
* Functions cannot have generic parameters.
* Functions cannot be variadic.
- `ocaml-interop-inspect` crate to inspect runtime OCaml values.

## [0.12.0] - 2025-05-16

Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ members = [
".",
"derive",
"dune-builder",
"inspect",
"inspect/examples/inspect_runtime_example",
"testing/rust-caller",
"testing/ocaml-caller/rust",
"docs/examples/tuples/rust",
Expand Down
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ test:
cargo test
@echo "Running derive crate tests..."
cargo test -p ocaml-interop-derive
@echo "Running OCaml interop inspect tests..."
cargo test -p ocaml-interop-inspect

test-examples:
@echo "Running rust-caller tests..."
Expand All @@ -23,6 +25,9 @@ test-examples:
@echo "Running Noalloc Primitives example (docs/examples/noalloc_primitives)..."
cd docs/examples/noalloc_primitives; opam exec -- dune test -f
@echo "--- Finished Documentation Examples ---"
@echo "Running OCaml interop inspect examples..."
cargo test -p ocaml-interop-inspect --example basic_usage
cd inspect/examples/inspect_runtime_example; cargo run

test-all: test test-examples
@echo "All tests (crate and examples) completed."
29 changes: 29 additions & 0 deletions inspect/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[package]
name = "ocaml-interop-inspect"
version = "0.12.0"
authors = ["Bruno Deferrari <utizoc@gmail.com>"]
license = "MIT"
description = "Runtime value inspection utilities for OCaml interop debugging"
homepage = "https://github.com/tizoc/ocaml-interop"
repository = "https://github.com/tizoc/ocaml-interop"
keywords = ["ocaml", "rust", "ffi", "interop", "debug"]
edition = "2021"

[dependencies]
ocaml-sys = { version = "0.26", features = ["ocaml5"] }

[dev-dependencies]
ocaml-interop = { path = ".." }

[[example]]
name = "basic_usage"
path = "examples/basic_usage.rs"

[[example]]
name = "inspect_runtime_example"
path = "examples/inspect_runtime_example/src/main.rs"
required-features = ["inspect-runtime-example"]

[features]
default = []
inspect-runtime-example = []
140 changes: 140 additions & 0 deletions inspect/examples/basic_usage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Copyright (c) Viable Systems and TezEdge Contributors
// SPDX-License-Identifier: MIT

//! Basic usage examples of the OCaml value inspector.

#[cfg(test)]
use ocaml_interop::*;
use ocaml_interop_inspect::inspect_raw_value;

// Example function that demonstrates how to use the inspector for debugging
#[cfg(test)]
fn debug_ocaml_conversion<T>(cr: &mut OCamlRuntime, value: OCaml<'_, T>, description: &str) {
println!("=== Debugging OCaml value: {} ===", description);

// Get the raw OCaml value and inspect it
let inspection = unsafe { inspect_raw_value(value.raw()) };

// Show the full detailed representation
println!("Full details: {}", inspection);

// Show the compact representation for quick viewing
println!("Compact: {}", inspection.compact());

// Show the Debug representation with extra details
println!("Debug: {:?}", inspection);

// Check the type
println!("Type: {}", inspection.repr().type_name());

println!();
}

// Example function showing how to use the inspector in error handling
#[cfg(test)]
fn safe_conversion_with_debugging<T, R>(
cr: &mut OCamlRuntime,
value: OCaml<'_, T>,
) -> Result<R, String>
where
R: FromOCaml<T>,
{
// First, inspect the value to understand its structure
let inspection = unsafe { inspect_raw_value(value.raw()) };

// Try the conversion
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| value.to_rust::<R>())) {
Ok(result) => {
println!("✓ Conversion successful for: {}", inspection.compact());
Ok(result)
}
Err(_) => {
let error_msg = format!(
"✗ Conversion failed for value with structure: {}\nFull details: {}",
inspection.compact(),
inspection
);
Err(error_msg)
}
}
}

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

#[test]
fn example_inspect_integer() {
// This example shows how to inspect OCaml integers
let inspection = unsafe {
inspect_raw_value((42_isize << 1) | 1) // OCaml integer encoding
};

assert!(inspection.repr().is_immediate());
assert!(inspection.compact().contains("integer 42"));
println!("Integer inspection: {}", inspection);
}

#[test]
fn example_inspect_unit() {
let inspection = unsafe { inspect_raw_value(ocaml_sys::UNIT) };

assert!(inspection.repr().is_immediate());
assert!(inspection.compact().contains("unit"));
println!("Unit inspection: {}", inspection);
}

#[test]
fn example_inspect_boolean() {
let true_inspection = unsafe { inspect_raw_value(ocaml_sys::TRUE) };

let false_inspection = unsafe { inspect_raw_value(ocaml_sys::FALSE) };

assert!(true_inspection.repr().is_immediate());
assert!(false_inspection.repr().is_immediate());
assert!(true_inspection.compact().contains("true"));
assert!(false_inspection.compact().contains("false"));

println!("True inspection: {}", true_inspection);
println!("False inspection: {}", false_inspection);
}
}

fn main() {
println!("OCaml Value Inspector Examples");
println!("==============================");

// Examples of inspecting different types of OCaml values

// Integer inspection
println!("1. Integer values:");
let int_inspection = unsafe { inspect_raw_value((123_isize << 1) | 1) };
println!(" {}", int_inspection);
println!(" Compact: {}", int_inspection.compact());

// Boolean inspection
println!("\n2. Boolean values:");
let true_inspection = unsafe { inspect_raw_value(ocaml_sys::TRUE) };
let false_inspection = unsafe { inspect_raw_value(ocaml_sys::FALSE) };
println!(" True: {}", true_inspection);
println!(" False: {}", false_inspection);

// Unit inspection
println!("\n3. Unit value:");
let unit_inspection = unsafe { inspect_raw_value(ocaml_sys::UNIT) };
println!(" {}", unit_inspection);

// Empty list inspection
println!("\n4. Empty list:");
let empty_list_inspection = unsafe { inspect_raw_value(ocaml_sys::EMPTY_LIST) };
println!(" {}", empty_list_inspection);

// None option inspection
println!("\n5. None option:");
let none_inspection = unsafe { inspect_raw_value(ocaml_sys::NONE) };
println!(" {}", none_inspection);

println!("\nFor block values (tuples, records, variants), you'll need to");
println!("create them through the OCaml runtime within a proper context.");
println!("This example shows the basic immediate value inspection capabilities.");
}
13 changes: 13 additions & 0 deletions inspect/examples/inspect_runtime_example/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "inspect-runtime-example"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies]
ocaml-interop = { path = "../../.." }
ocaml-interop-inspect = { path = "../.." }

[build-dependencies]
cc = "1"
ocaml-interop-dune-builder = { path = "../../../dune-builder" }
23 changes: 23 additions & 0 deletions inspect/examples/inspect_runtime_example/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) Viable Systems and TezEdge Contributors
// SPDX-License-Identifier: MIT

use ocaml_interop_dune_builder::DuneBuilder;

fn main() {
let ocaml_dir = "ocaml";

// Rebuild if the OCaml source code changes
println!("cargo:rerun-if-changed={}/test_types.ml", ocaml_dir);
println!("cargo:rerun-if-changed={}/dune", ocaml_dir);

// Build the OCaml code using dune
let dune_builder = DuneBuilder::new(ocaml_dir);
let objects = dune_builder.build("test_types.exe.o");

let mut build = cc::Build::new();
for object in objects {
build.object(object);
}

build.compile("callable_ocaml");
}
4 changes: 4 additions & 0 deletions inspect/examples/inspect_runtime_example/ocaml/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(executables
(names test_types)
(libraries threads)
(modes object))
77 changes: 77 additions & 0 deletions inspect/examples/inspect_runtime_example/ocaml/test_types.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
(* Copyright (c) Viable Systems and TezEdge Contributors
SPDX-License-Identifier: MIT *)

(* Define various OCaml types to test the inspector *)

(* Simple record with different field types *)
type test_record = {
int_field: int;
float_field: float;
string_field: string;
bool_field: bool;
tuple_field: int * string * float;
}

(* Variant type *)
type test_variant =
| Empty
| WithInt of int
| WithString of string
| WithTuple of int * float
| Complex of test_record

(* List of variants *)
type nested_structure = test_variant list

(* Polymorphic variant *)
type poly_variant = [
| `None
| `Int of int
| `Tuple of int * string
]

(* Function to create test instances *)
let create_test_record () =
{
int_field = 42;
float_field = 3.14159;
string_field = "Hello, OCaml!";
bool_field = true;
tuple_field = (123, "tuple element", 45.67)
}

let create_empty_variant () = Empty

let create_int_variant () = WithInt 42

let create_string_variant () = WithString "variant string"

let create_tuple_variant () = WithTuple (42, 3.14)

let create_complex_variant () = Complex (create_test_record ())

let create_list_of_variants () = [
Empty;
WithInt 42;
WithString "variant in list";
WithTuple (99, 99.99)
]

let create_poly_none () = `None

let create_poly_int () = `Int 42

let create_poly_tuple () = `Tuple (42, "poly tuple")

(* Register all functions for Rust to call *)
let () =
Callback.register "create_test_record" create_test_record;
Callback.register "create_empty_variant" create_empty_variant;
Callback.register "create_int_variant" create_int_variant;
Callback.register "create_string_variant" create_string_variant;
Callback.register "create_tuple_variant" create_tuple_variant;
Callback.register "create_complex_variant" create_complex_variant;
Callback.register "create_list_of_variants" create_list_of_variants;
Callback.register "create_poly_none" create_poly_none;
Callback.register "create_poly_int" create_poly_int;
Callback.register "create_poly_tuple" create_poly_tuple
Loading
Loading