Implement Rust-side types.

This commit is contained in:
Alejandro Cabeza Romero 2026-04-24 12:04:05 +02:00
parent f70a866ad6
commit a291e1cb54
No known key found for this signature in database
GPG Key ID: DA3D14AE478030FD
7 changed files with 181 additions and 2 deletions

View File

@ -13,4 +13,60 @@ name = "logos-blockchain-circuits-types"
version = "0.1.0"
dependencies = [
"libc",
"thiserror",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"

View File

@ -5,3 +5,4 @@ edition = "2024"
[dependencies]
libc = "0.2.185"
thiserror = "2.0.18"

View File

@ -0,0 +1,13 @@
use crate::ffi;
pub struct Bytes(Vec<u8>);
impl From<ffi::Bytes> for Bytes {
fn from(mut ffi_value: ffi::Bytes) -> Self {
let raw = unsafe {
std::slice::from_raw_parts(ffi_value.data, ffi_value.size).to_vec()
};
unsafe { ffi::free_bytes(&mut ffi_value); }
Self(raw)
}
}

View File

@ -1,3 +1,7 @@
pub mod bytes;
pub mod status;
pub mod witness_input;
pub use bytes::Bytes;
pub use status::{Result, Error};
pub use witness_input::WitnessInput;

View File

@ -0,0 +1,43 @@
use std::ffi::CStr;
use thiserror::Error;
use crate::ffi::status::Code as FfiStatusCode;
pub type DynError = Box<dyn std::error::Error + Send + Sync>;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Error)]
pub enum Error {
#[error("Invalid input")]
InvalidInput,
#[error("Out of memory")]
OutOfMemory,
#[error(transparent)]
Other(#[from] DynError),
}
impl TryFrom<crate::ffi::Status> for () {
type Error = Error;
fn try_from(status: crate::ffi::Status) -> Result<()> {
match status.code {
FfiStatusCode::Ok => Ok(()),
FfiStatusCode::DynError => {
let message: Option<&CStr> =
if status.has_message() {
let status_message = unsafe {
CStr::from_ptr(status.message.as_ptr())
};
Some(status_message)
} else {
None
};
let error_message = message
.map(|inner| DynError::from(inner.to_string_lossy().to_owned()))
.unwrap_or_else(|| DynError::from("Unknown error"));
Err(Error::Other(error_message))
},
FfiStatusCode::InvalidInput => Err(Error::InvalidInput),
FfiStatusCode::OutOfMemory => Err(Error::OutOfMemory),
}
}
}

View File

@ -0,0 +1,57 @@
use std::ffi::{c_char, CString};
use crate::ffi;
pub struct WitnessInput {
dat: Vec<u8>,
inputs_json: String,
}
impl WitnessInput {
#[must_use]
pub const fn new(dat: Vec<u8>, inputs_json: String) -> Self {
Self { dat, inputs_json }
}
#[must_use]
pub fn as_ffi(&'_ self) -> WitnessInputFfiGuard<'_> {
WitnessInputFfiGuard::new(self)
}
}
/// Represents a guard for managing the lifetime of a WitnessInput in FFI.
/// This struct ensures that the memory allocated for the FFI representation of WitnessInput is
/// properly released when it goes out of scope.
pub struct WitnessInputFfiGuard<'a> {
ffi: ffi::WitnessInput,
_lifetime: std::marker::PhantomData<&'a WitnessInput>,
}
impl<'a> WitnessInputFfiGuard<'a> {
#[must_use]
pub fn new(inner: &'a WitnessInput) -> Self {
let dat = ffi::ConstBytes { data: inner.dat.as_ptr(), size: inner.dat.len() };
let inputs_json = CString::new(inner.inputs_json.clone()).expect("CString::new failed").into_raw();
let ffi = ffi::WitnessInput { dat, inputs_json };
Self {
ffi,
_lifetime: std::marker::PhantomData,
}
}
}
impl<'a> AsRef<ffi::WitnessInput> for WitnessInputFfiGuard<'a> {
fn as_ref(&self) -> &ffi::WitnessInput {
&self.ffi
}
}
impl<'a> Drop for WitnessInputFfiGuard<'a> {
fn drop(&mut self) {
if !self.ffi.inputs_json.is_null() {
drop(unsafe {
CString::from_raw(self.ffi.inputs_json as *mut c_char)
})
}
}
}

View File

@ -1,4 +1,5 @@
use std::cmp::PartialEq;
use std::ffi::c_char;
#[repr(C)]
#[derive(Debug, PartialEq)]
@ -23,14 +24,14 @@ impl Code {
#[derive(Debug)]
pub struct Status {
pub code: Code,
pub message: String,
pub message: [c_char; 256],
}
impl Status {
pub fn ok() -> Self {
Status {
code: Code::Ok,
message: String::new(),
message: [0; 256],
}
}
@ -41,4 +42,8 @@ impl Status {
pub fn is_error(&self) -> bool {
self.code.is_error()
}
pub fn has_message(&self) -> bool {
self.message[0] != 0
}
}