1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
use std::{convert::TryFrom, mem, u32}; use types::{ bytesrepr::{self, FromBytes, ToBytes}, CLValue, }; use crate::{account::Account, contract::Contract, TypeMismatch}; #[repr(u8)] enum Tag { CLValue = 0, Account = 1, Contract = 2, } #[derive(Eq, PartialEq, Clone, Debug)] pub enum StoredValue { CLValue(CLValue), Account(Account), Contract(Contract), } impl StoredValue { pub fn as_cl_value(&self) -> Option<&CLValue> { match self { StoredValue::CLValue(cl_value) => Some(cl_value), _ => None, } } pub fn as_account(&self) -> Option<&Account> { match self { StoredValue::Account(account) => Some(account), _ => None, } } pub fn as_contract(&self) -> Option<&Contract> { match self { StoredValue::Contract(contract) => Some(contract), _ => None, } } pub fn type_name(&self) -> String { match self { StoredValue::CLValue(cl_value) => format!("{:?}", cl_value.cl_type()), StoredValue::Account(_) => "Account".to_string(), StoredValue::Contract(_) => "Contract".to_string(), } } } impl TryFrom<StoredValue> for CLValue { type Error = TypeMismatch; fn try_from(stored_value: StoredValue) -> Result<Self, Self::Error> { match stored_value { StoredValue::CLValue(cl_value) => Ok(cl_value), _ => Err(TypeMismatch::new( "CLValue".to_string(), stored_value.type_name(), )), } } } impl TryFrom<StoredValue> for Account { type Error = TypeMismatch; fn try_from(stored_value: StoredValue) -> Result<Self, Self::Error> { match stored_value { StoredValue::Account(account) => Ok(account), _ => Err(TypeMismatch::new( "Account".to_string(), stored_value.type_name(), )), } } } impl TryFrom<StoredValue> for Contract { type Error = TypeMismatch; fn try_from(stored_value: StoredValue) -> Result<Self, Self::Error> { match stored_value { StoredValue::Contract(contract) => Ok(contract), _ => Err(TypeMismatch::new( "Contract".to_string(), stored_value.type_name(), )), } } } fn to_bytes<T: ToBytes>(value: &T, tag: Tag) -> Result<Vec<u8>, bytesrepr::Error> { let mut bytes = value.to_bytes()?; if bytes.len() >= u32::max_value() as usize - mem::size_of::<Tag>() { return Err(bytesrepr::Error::OutOfMemory); } let mut result = Vec::with_capacity(bytes.len() + mem::size_of::<Tag>()); result.push(tag as u8); result.append(&mut bytes); Ok(result) } impl ToBytes for StoredValue { fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> { match self { StoredValue::CLValue(cl_value) => to_bytes(cl_value, Tag::CLValue), StoredValue::Account(account) => to_bytes(account, Tag::Account), StoredValue::Contract(contract) => to_bytes(contract, Tag::Contract), } } } impl FromBytes for StoredValue { fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> { let (tag, remainder): (u8, &[u8]) = FromBytes::from_bytes(bytes)?; match tag { tag if tag == Tag::CLValue as u8 => CLValue::from_bytes(remainder) .map(|(cl_value, remainder)| (StoredValue::CLValue(cl_value), remainder)), tag if tag == Tag::Account as u8 => Account::from_bytes(remainder) .map(|(account, remainder)| (StoredValue::Account(account), remainder)), tag if tag == Tag::Contract as u8 => Contract::from_bytes(remainder) .map(|(contract, remainder)| (StoredValue::Contract(contract), remainder)), _ => Err(bytesrepr::Error::Formatting), } } } pub mod gens { use proptest::prelude::*; use types::gens::cl_value_arb; use super::StoredValue; use crate::{account::gens::account_arb, contract::gens::contract_arb}; pub fn stored_value_arb() -> impl Strategy<Value = StoredValue> { prop_oneof![ cl_value_arb().prop_map(StoredValue::CLValue), account_arb().prop_map(StoredValue::Account), contract_arb().prop_map(StoredValue::Contract), ] } }