//! ToHaskell and FromHaskell instances for the various standard types mandated //! by the [Borsh spec](https://borsh.io/), piggy-backing on the implementation //! in the `borsh` crate. The only spec-described types _not_ provided are //! user-defined structs and enums. use borsh::{BorshDeserialize, BorshSerialize}; use std::{ collections::{HashMap, HashSet}, hash::Hash, io::{ErrorKind, Write}, marker::PhantomData, }; use crate::{ derive_array_instances, derive_simple_instances, derive_tuple_instances, deriving_via::{tag_ref, untag_val, Haskell}, error::Result, from_haskell::FromHaskell, map_tuple, map_tuple_ref, to_haskell::ToHaskell, HaskellSize, }; /******************************************************************************* Simple (non-composite) instances *******************************************************************************/ derive_simple_instances!(u8); derive_simple_instances!(u16); derive_simple_instances!(u32); derive_simple_instances!(u64); derive_simple_instances!(u128); derive_simple_instances!(i8); derive_simple_instances!(i16); derive_simple_instances!(i32); derive_simple_instances!(i64); derive_simple_instances!(i128); derive_simple_instances!(f32); derive_simple_instances!(f64); derive_simple_instances!(()); derive_simple_instances!(String); /******************************************************************************* Array instances This is the same set of sizes as supported by borsh. *******************************************************************************/ derive_array_instances!(0); derive_array_instances!(1); derive_array_instances!(2); derive_array_instances!(3); derive_array_instances!(4); derive_array_instances!(5); derive_array_instances!(6); derive_array_instances!(7); derive_array_instances!(8); derive_array_instances!(9); derive_array_instances!(10); derive_array_instances!(11); derive_array_instances!(12); derive_array_instances!(13); derive_array_instances!(14); derive_array_instances!(15); derive_array_instances!(16); derive_array_instances!(17); derive_array_instances!(18); derive_array_instances!(19); derive_array_instances!(20); derive_array_instances!(21); derive_array_instances!(22); derive_array_instances!(23); derive_array_instances!(24); derive_array_instances!(25); derive_array_instances!(26); derive_array_instances!(27); derive_array_instances!(28); derive_array_instances!(29); derive_array_instances!(30); derive_array_instances!(31); derive_array_instances!(32); derive_array_instances!(64); derive_array_instances!(65); derive_array_instances!(128); derive_array_instances!(256); derive_array_instances!(512); derive_array_instances!(1024); derive_array_instances!(2048); /******************************************************************************* Composite instances This is the same set of tuple sizes as supported by `borsh.` *******************************************************************************/ derive_tuple_instances!(T0, T1); derive_tuple_instances!(T0, T1, T2); derive_tuple_instances!(T0, T1, T2, T3); derive_tuple_instances!(T0, T1, T2, T3, T4); derive_tuple_instances!(T0, T1, T2, T3, T4, T5); derive_tuple_instances!(T0, T1, T2, T3, T4, T5, T6); derive_tuple_instances!(T0, T1, T2, T3, T4, T5, T6, T7); derive_tuple_instances!(T0, T1, T2, T3, T4, T5, T6, T7, T8); derive_tuple_instances!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9); derive_tuple_instances!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); derive_tuple_instances!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11); derive_tuple_instances!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12); derive_tuple_instances!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13); derive_tuple_instances!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14); derive_tuple_instances!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15); derive_tuple_instances!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16); derive_tuple_instances!( T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 ); derive_tuple_instances!( T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 ); derive_tuple_instances!( T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 ); /******************************************************************************* Vec *******************************************************************************/ impl> ToHaskell for Vec { fn to_haskell(&self, writer: &mut W, _: PhantomData) -> Result<()> { let tagged: Vec<&Haskell> = self.iter().map(tag_ref).collect(); tagged.serialize(writer)?; Ok(()) } } impl> FromHaskell for Vec { fn from_haskell(buf: &mut &[u8], _: PhantomData) -> Result { let tagged: Vec> = BorshDeserialize::deserialize(buf)?; Ok(tagged.into_iter().map(untag_val).collect()) } } /******************************************************************************* HashMap *******************************************************************************/ impl ToHaskell for HashMap where K: Eq + PartialOrd + Hash + ToHaskell, V: ToHaskell, { fn to_haskell(&self, writer: &mut W, _: PhantomData) -> Result<()> { let tagged: HashMap<&Haskell, &Haskell> = self.iter().map(|(k, v)| (tag_ref(k), tag_ref(v))).collect(); tagged.serialize(writer)?; Ok(()) } } impl FromHaskell for HashMap where K: Eq + Hash + FromHaskell, V: FromHaskell, { fn from_haskell(buf: &mut &[u8], _: PhantomData) -> Result { let tagged: HashMap, Haskell> = BorshDeserialize::deserialize(buf)?; Ok(tagged .into_iter() .map(|(k, v)| (untag_val(k), untag_val(v))) .collect()) } } /******************************************************************************* HashSet *******************************************************************************/ impl ToHaskell for HashSet where T: Eq + PartialOrd + Hash + ToHaskell, { fn to_haskell(&self, writer: &mut W, _: PhantomData) -> Result<()> { let tagged: HashSet<&Haskell> = self.iter().map(tag_ref).collect(); tagged.serialize(writer)?; Ok(()) } } impl FromHaskell for HashSet where T: Eq + Hash + FromHaskell, { fn from_haskell(buf: &mut &[u8], _: PhantomData) -> Result { let tagged: HashSet> = BorshDeserialize::deserialize(buf)?; Ok(tagged.into_iter().map(untag_val).collect()) } } /******************************************************************************* Option *******************************************************************************/ impl> ToHaskell for Option { fn to_haskell(&self, writer: &mut W, _: PhantomData) -> Result<()> { let tagged: Option<&Haskell> = self.as_ref().map(tag_ref); tagged.serialize(writer)?; Ok(()) } } impl> FromHaskell for Option { fn from_haskell(buf: &mut &[u8], _: PhantomData) -> Result { let tagged: Option> = BorshDeserialize::deserialize(buf)?; Ok(tagged.map(untag_val)) } } /******************************************************************************* Result `Result` is not explicitly mentioned by the Borsh spec, but it's ubiquitous and so we provide an instance for it, following the standard rule for enum. There is no need for an instance of `FromHaskell`, since this is indicating the result of some Rust-side operation. *******************************************************************************/ impl, E: ToHaskell> ToHaskell for core::result::Result { fn to_haskell(&self, writer: &mut W, _: PhantomData) -> Result<()> { let tagged: core::result::Result<&Haskell, &Haskell> = match self { Ok(t) => Ok(tag_ref(t)), Err(e) => Err(tag_ref(e)), }; tagged.serialize(writer)?; Ok(()) } } /******************************************************************************* Bool The Borsh spec does not mention Bool; we encode `true` as 1 and `false` as 0; this matches what the Haskell `borsh` library does. *******************************************************************************/ impl HaskellSize for bool { fn haskell_size(tag: PhantomData) -> usize { u8::haskell_size(tag) } } impl ToHaskell for bool { fn to_haskell(&self, writer: &mut W, tag: PhantomData) -> Result<()> { let as_u8: u8 = if *self { 1 } else { 0 }; as_u8.to_haskell(writer, tag) } } impl FromHaskell for bool { fn from_haskell(buf: &mut &[u8], tag: PhantomData) -> Result { let as_u8 = u8::from_haskell(buf, tag)?; match as_u8 { 0 => Ok(false), 1 => Ok(true), _ => Err(Box::new(std::io::Error::new( ErrorKind::InvalidData, "Invalid bool", ))), } } }