use std::fmt;
use hex::FromHexError;
use serde::{Deserialize, Serialize};
use serde_json::json;
use snafu::{Snafu, OptionExt};
use crate::{
AccountName, ActionName, Contract,
PermissionName, Name, ABISerializable,
abiserializable::to_bin, Bytes, JsonValue,
ByteStream, ABI, ABIError, InvalidName,
abi, with_location, impl_auto_error_conversion,
};
extern crate self as kudu;
#[derive(Eq, Hash, PartialEq, 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)
}]
}
}
impl fmt::Display for PermissionLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}@{}", self.actor, self.permission)
}
}
impl fmt::Debug for PermissionLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}@{}", self.actor, self.permission)
}
}
#[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("could not match JSON object to action"))]
FromJson { source: serde_json::Error },
#[snafu(display("ABI error"))]
ABI { source: ABIError },
#[snafu(display("Invalid value: {message}"))]
InvalidValue { message: &'static str },
}
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, 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(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 abi = abi::registry::get_abi(account)?;
let mut ds = Bytes::new();
abi.encode_variant(&mut ds, action_name, data)?;
ds
};
Ok(Action {
account: Name::new(account)?,
name: Name::new(action_name)?,
authorization,
data,
})
}
pub fn from_json_array(actions: &JsonValue) -> Result<Vec<Action>, ActionError> {
let array = actions.as_array()
.context(InvalidValueSnafu { message: "cannot create Vec<Action> from JSON value that is not an array" })?;
array.iter()
.map(Action::from_json)
.collect()
}
pub fn decode_data(&self) -> Result<JsonValue, ABIError> {
let abi = abi::registry::get_abi(&self.account.to_string())?;
self.decode_data_with_abi(&abi)
}
pub fn decode_data_with_abi(&self, abi: &ABI) -> Result<JsonValue, ABIError> {
let mut ds = ByteStream::from(&self.data);
abi.decode_variant(&mut ds, &self.name.to_string())
}
pub fn with_data(mut self, value: &JsonValue) -> Result<Self, ActionError> {
let mut ds = Bytes::new();
let abi = abi::registry::get_abi(&self.account.to_string())?;
abi.encode_variant(&mut ds, &self.name.to_string(), value)?;
self.data = ds;
Ok(self)
}
pub fn to_json(&self) -> Result<JsonValue, ABIError> {
let abi = abi::registry::get_abi(&self.account.to_string())?;
self.to_json_with_abi(&abi)
}
pub fn to_json_with_abi(&self, abi: &ABI) -> Result<JsonValue, ABIError> {
Ok(json!({
"account": self.account.to_string(),
"name": self.name.to_string(),
"authorization": serde_json::to_value(&self.authorization)?,
"data": self.decode_data_with_abi(abi)?,
}))
}
}
impl fmt::Debug for Action {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Action({}::{} {:?} data={}", self.account, self.name, self.authorization, self.data.to_hex())
}
}
impl fmt::Display for Action {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}