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: 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) -> Result; fn from_haskell_slice(slice: &[u8], tag: PhantomData) -> Result { 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(inp: *const u8, len: usize, tag: PhantomData) -> T where T: FromHaskell, { let mut vec: Vec = 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( inp: *const u8, inp_len: usize, tag: PhantomData, ) -> T where T: FromHaskell + HaskellSize, { 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) } }