haskell-rust-ffi/haskell-ffi/src/from_haskell.rs
Edsko de Vries 7685f363e2 Use std::error::Error (not std::io::Error)
This makes client code a bit easier to write, since we can then use `?;` in
more situations to just cast any kind of error.
2023-03-21 16:00:58 +01:00

76 lines
2.3 KiB
Rust

use std::{io::ErrorKind, marker::PhantomData};
use crate::{error::Error, HaskellSize};
/*******************************************************************************
Main class definition
*******************************************************************************/
const ERROR_NOT_ALL_BYTES_READ: &str = "Not all bytes read";
pub trait FromHaskell<Tag>: Sized {
/// Deserialize data sent from Haskell
///
/// This is the analogue of `BorshDeserialize::deserialize`.
//
/// See `ToHaskell` for a detailed discussion of the `tag` argument.
fn from_haskell(buf: &mut &[u8], tag: PhantomData<Tag>) -> Result<Self, Error>;
fn from_haskell_slice(slice: &[u8], tag: PhantomData<Tag>) -> Result<Self, Error> {
let mut slice_mut = slice;
let result = Self::from_haskell(&mut slice_mut, tag)?;
if !slice_mut.is_empty() {
return Err(Box::new(std::io::Error::new(
ErrorKind::InvalidData,
ERROR_NOT_ALL_BYTES_READ,
)));
}
Ok(result)
}
}
/*******************************************************************************
Derived functionality
See comments in `to_haskell` for why these functions do not live inside the
trait.
*******************************************************************************/
/// Marshall value with variable-sized encoding
pub fn marshall_from_haskell_var<Tag, T>(inp: *const u8, len: usize, tag: PhantomData<Tag>) -> T
where
T: FromHaskell<Tag>,
{
let mut vec: Vec<u8> = vec![0; len];
unsafe {
std::ptr::copy(inp, vec.as_mut_ptr(), len);
}
match T::from_haskell_slice(vec.as_ref(), tag) {
Ok(t) => t,
Err(e) => panic!("{}", e),
}
}
/// Marshall value with fixed-size encoding
///
/// The `len` argument here is only to verify that the Haskell-side and
/// Rust-side agree on the size of the encoding.
pub fn marshall_from_haskell_fixed<Tag, T>(
inp: *const u8,
inp_len: usize,
tag: PhantomData<Tag>,
) -> T
where
T: FromHaskell<Tag> + HaskellSize<Tag>,
{
let expected_len = T::haskell_size(tag);
if inp_len != expected_len {
panic!(
"expected buffer of size {}, but got {}",
expected_len, inp_len
)
} else {
marshall_from_haskell_var(inp, inp_len, tag)
}
}