Created
April 24, 2025 18:12
-
-
Save jmsdnns/7923459f7f24022c3710da88636bb888 to your computer and use it in GitHub Desktop.
Using Rust library to parse yaml from Ocaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use serde::{Deserialize, Serialize}; | |
use serde_yaml::Value; | |
use std::ffi::{CStr, CString}; | |
use std::os::raw::c_char; | |
#[repr(C)] | |
pub struct KeyValue { | |
pub key: *const c_char, | |
pub value: *const c_char, | |
} | |
#[allow(clippy::not_unsafe_ptr_arg_deref)] | |
#[unsafe(no_mangle)] | |
pub extern "C" fn parse_yaml(input: *const u8, len: usize) -> *mut KeyValue { | |
if input.is_null() { | |
return std::ptr::null_mut(); | |
} | |
let slice = unsafe { std::slice::from_raw_parts(input, len) }; | |
let yaml_str = match std::str::from_utf8(slice) { | |
Ok(s) => s, | |
Err(_) => return std::ptr::null_mut(), | |
}; | |
let parsed: Result<Value, serde_yaml::Error> = serde_yaml::from_str(yaml_str); | |
match parsed { | |
Ok(Value::Mapping(mapping)) => { | |
if let Some((key, value)) = mapping.into_iter().next() { | |
let key_str = match key.as_str() { | |
Some(s) => s, | |
None => return std::ptr::null_mut(), | |
}; | |
let value_str = match value.as_str() { | |
Some(s) => s, | |
None => return std::ptr::null_mut(), | |
}; | |
let key_cstr = CString::new(key_str).unwrap(); | |
let value_cstr = CString::new(value_str).unwrap(); | |
let kv = KeyValue { | |
key: key_cstr.into_raw(), | |
value: value_cstr.into_raw(), | |
}; | |
Box::into_raw(Box::new(kv)) | |
} else { | |
std::ptr::null_mut() | |
} | |
} | |
_ => std::ptr::null_mut(), | |
} | |
} | |
#[allow(clippy::not_unsafe_ptr_arg_deref)] | |
#[unsafe(no_mangle)] | |
pub extern "C" fn free_key_value(kv: *mut KeyValue) { | |
if kv.is_null() { | |
return; | |
} | |
unsafe { | |
// frees the pointer | |
let kv_box = Box::from_raw(kv); | |
if !kv_box.key.is_null() { | |
let _ = CString::from_raw(kv_box.key as *mut c_char); | |
} | |
if !kv_box.value.is_null() { | |
let _ = CString::from_raw(kv_box.value as *mut c_char); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(* | |
ocamlfind ocamlopt -linkpkg -package ctypes.foreign yoml.ml -o main | |
LD_LIBRARY_PATH=../yamlrs/target/debug ./main | |
*) | |
open Ctypes | |
open Foreign | |
let lib = | |
Dl.dlopen ~filename:"../yamlrs/target/debug/libyamlrs.so" | |
~flags:[ Dl.RTLD_NOW ] | |
(* matches rust struct *) | |
type key_value | |
let key_value : key_value structure typ = structure "KeyValue" | |
let key = field key_value "key" (ptr char) | |
let value = field key_value "value" (ptr char) | |
let () = seal key_value | |
(* bindings for both rust functions *) | |
let parse_yaml = | |
foreign ~from:lib "parse_yaml" | |
(ptr uint8_t @-> size_t @-> returning (ptr_opt key_value)) | |
let free_key_value = | |
foreign ~from:lib "free_key_value" (ptr key_value @-> returning void) | |
(* converts c-string to ocaml string *) | |
let string_from_c_ptr (p : char ptr) : string = | |
if p = from_voidp char null then "" else coerce (ptr char) string p | |
(* the parse function *) | |
let parse str = | |
let len = String.length str in | |
let buf = CArray.of_string str in | |
let input_ptr = coerce (ptr char) (ptr uint8_t) (CArray.start buf) in | |
match parse_yaml input_ptr (Unsigned.Size_t.of_int len) with | |
| None -> None | |
| Some kv_ptr -> | |
let kv = !@kv_ptr in | |
let k = string_from_c_ptr (getf kv key) in | |
let v = string_from_c_ptr (getf kv value) in | |
free_key_value kv_ptr; | |
Some (k, v) | |
let () = | |
let yaml = "name: ocaml" in | |
match parse yaml with | |
| Some (k, v) -> Printf.printf "Parsed: %s => %s\n" k v | |
| None -> Printf.printf "Failed to parse YAML\n" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment