use hex::FromHexError;
use serde::{Deserialize, Serialize};
use serde_json::json;
use snafu::{Snafu, OptionExt, ensure};
use crate::{
AccountName, ActionName, Contract,
PermissionName, Name, ABISerializable,
abiserializable::to_bin, Bytes, JsonValue,
ByteStream, ABIProvider, ABIError, InvalidName,
with_location, impl_auto_error_conversion,
};
extern crate self as kudu;
#[derive(Eq, Hash, PartialEq, Debug, Copy, Clone, Default, Deserialize, Serialize, ABISerializable)]
pub struct PermissionLevel {
pub actor: AccountName,
pub permission: PermissionName,
}
pub trait IntoPermissionVec {
fn into_permission_vec(self) -> Vec<PermissionLevel>;
}
impl IntoPermissionVec for Vec<PermissionLevel> {
fn into_permission_vec(self) -> Vec<PermissionLevel> {
self
}
}
impl IntoPermissionVec for PermissionLevel {
fn into_permission_vec(self) -> Vec<PermissionLevel> {
vec![self]
}
}
impl IntoPermissionVec for (&str, &str) {
fn into_permission_vec(self) -> Vec<PermissionLevel> {
vec![PermissionLevel {
actor: AccountName::constant(self.0),
permission: PermissionName::constant(self.1)
}]
}
}
#[with_location]
#[derive(Debug, Snafu)]
pub enum ActionError {
#[snafu(display("Cannot convert action['{field_name}'] to str, actual type: {value:?}"))]
FieldType {
field_name: String,
value: JsonValue,
},
#[snafu(display("Invalid name"))]
Name { source: InvalidName },
#[snafu(display("invalid hex representation"))]
FromHex { source: FromHexError },
#[snafu(display("missing ABIProvider, required to encode action data"))]
MissingABIProvider,
#[snafu(display("could not match JSON object to transaction"))]
FromJson { source: serde_json::Error },
#[snafu(display("ABI error"))]
ABI { source: ABIError },
}
impl_auto_error_conversion!(InvalidName, ActionError, NameSnafu);
impl_auto_error_conversion!(FromHexError, ActionError, FromHexSnafu);
impl_auto_error_conversion!(ABIError, ActionError, ABISnafu);
impl_auto_error_conversion!(serde_json::Error, ActionError, FromJsonSnafu);
#[derive(Eq, Hash, PartialEq, Debug, Clone, Default, Deserialize, Serialize, ABISerializable)]
pub struct Action {
pub account: AccountName,
pub name: ActionName,
pub authorization: Vec<PermissionLevel>,
pub data: Bytes,
}
impl Action {
pub fn new<T: Contract>(authorization: impl IntoPermissionVec, contract: &T) -> Action {
Action {
account: T::account(),
name: T::name(),
authorization: authorization.into_permission_vec(),
data: to_bin(contract)
}
}
pub fn conv_action_field_str<'a>(
action: &'a JsonValue,
field: &str
) -> Result<&'a str, ActionError> {
action[field].as_str().with_context(|| FieldTypeSnafu {
field_name: field,
value: action[field].clone(),
})
}
pub fn from_json(abi_provider: Option<&ABIProvider>, action: &JsonValue) -> Result<Action, ActionError> {
let account = Action::conv_action_field_str(action, "account")?;
let action_name = Action::conv_action_field_str(action, "name")?;
let authorization: Vec<PermissionLevel> = serde_json::from_str(&action["authorization"].to_string())?;
let data: Bytes = if action["data"].is_string() {
Bytes::from_hex(action["data"].as_str().unwrap())? }
else {
let data = &action["data"];
let mut ds = ByteStream::new();
ensure!(abi_provider.is_some(), MissingABIProviderSnafu);
let abi = abi_provider.unwrap().get_abi(account)?; abi.encode_variant(&mut ds, action_name, data)?;
ds.into()
};
Ok(Action {
account: Name::new(account)?,
name: Name::new(action_name)?,
authorization,
data,
})
}
pub fn from_json_array(
abi_provider: Option<&ABIProvider>,
actions: &JsonValue
) -> Result<Vec<Action>, ActionError> {
Ok(actions.as_array().unwrap().iter()
.map(|v| Action::from_json(abi_provider, v).unwrap())
.collect())
}
pub fn decode_data(&self, abi_provider: &ABIProvider) -> JsonValue {
let mut ds = ByteStream::from(self.data.clone());
let abi = abi_provider.get_abi(&self.account.to_string()).unwrap();
abi.decode_variant(&mut ds, &self.name.to_string()).unwrap()
}
pub fn with_data(mut self, abi_provider: &ABIProvider, value: &JsonValue) -> Self {
let mut ds = ByteStream::new();
let abi = abi_provider.get_abi(&self.account.to_string()).unwrap();
abi.encode_variant(&mut ds, &self.name.to_string(), value).unwrap();
self.data = ds.into();
self
}
pub fn to_json(&self, abi_provider: &ABIProvider) -> JsonValue {
json!({
"account": self.account.to_string(),
"name": self.name.to_string(),
"authorization": serde_json::to_value(&self.authorization).unwrap(),
"data": self.decode_data(abi_provider),
})
}
}