use std::collections::HashMap;
use std::fmt;
use std::fmt::{Debug, Formatter};
use std::hash::Hash;
use num_bigint::{BigInt, BigUint};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use tonlib_core::cell::dict::{KeyReader, ValReader};
use tonlib_core::cell::BagOfCells;
use tonlib_core::TonAddress;
use crate::tl::error::TvmStackError;
use crate::tl::Base64Standard;
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
pub struct TvmSlice {
#[serde(with = "Base64Standard")]
pub bytes: Vec<u8>,
}
impl Debug for TvmSlice {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"TvmSlice{{ bytes: [{}]}}",
self.bytes
.iter()
.map(|&byte| format!("{:02X}", byte))
.collect::<Vec<_>>()
.join(""),
)?;
Ok(())
}
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
pub struct TvmCell {
#[serde(with = "Base64Standard")]
pub bytes: Vec<u8>,
}
impl Debug for TvmCell {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "TvmCell {{ bytes: 0x")?;
for byte in &self.bytes {
write!(f, "{:02x}", byte)?;
}
write!(f, " }}")
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
pub struct TvmNumber {
pub number: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
pub struct TvmTuple {
pub elements: Vec<TvmStackEntry>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
pub struct TvmList {
pub elements: Vec<TvmStackEntry>,
}
#[derive(Serialize, Deserialize, strum::Display, Debug, Clone, PartialEq, Eq, Hash)]
#[serde(tag = "@type")]
pub enum TvmStackEntry {
#[serde(rename = "tvm.stackEntrySlice")]
Slice { slice: TvmSlice },
#[serde(rename = "tvm.stackEntryCell")]
Cell { cell: TvmCell },
#[serde(rename = "tvm.stackEntryNumber")]
Number { number: TvmNumber },
#[serde(rename = "tvm.stackEntryTuple")]
Tuple { tuple: TvmTuple },
#[serde(rename = "tvm.stackEntryList")]
List { list: TvmList },
#[serde(rename = "tvm.stackEntryUnsupported")]
Unsupported {},
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TvmStack {
pub elements: Vec<TvmStackEntry>,
}
impl<'de> Deserialize<'de> for TvmStack {
fn deserialize<D>(deserializer: D) -> Result<TvmStack, D::Error>
where
D: Deserializer<'de>,
{
Deserialize::deserialize(deserializer).map(|e| TvmStack { elements: e })
}
}
impl Serialize for TvmStack {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.elements.serialize(serializer)
}
}
impl TvmStack {
pub fn new() -> TvmStack {
TvmStack {
elements: Vec::new(),
}
}
pub fn from(elements: &[TvmStackEntry]) -> TvmStack {
TvmStack {
elements: elements.to_vec(),
}
}
pub fn get_string(&self, index: usize) -> Result<String, TvmStackError> {
self.get(index, TvmStack::extract_string)
}
pub fn get_i32(&self, index: usize) -> Result<i32, TvmStackError> {
self.get(index, TvmStack::extract_i32)
}
pub fn get_i64(&self, index: usize) -> Result<i64, TvmStackError> {
self.get(index, TvmStack::extract_i64)
}
pub fn get_biguint(&self, index: usize) -> Result<BigUint, TvmStackError> {
self.get(index, TvmStack::extract_biguint)
}
pub fn get_bigint(&self, index: usize) -> Result<BigInt, TvmStackError> {
self.get(index, TvmStack::extract_bigint)
}
pub fn get_boc(&self, index: usize) -> Result<BagOfCells, TvmStackError> {
self.get(index, TvmStack::extract_boc)
}
pub fn get_address(&self, index: usize) -> Result<TonAddress, TvmStackError> {
self.get_boc(index)?
.single_root()?
.parse_fully(|r| r.load_address())
.map_err(TvmStackError::TonCellError)
}
pub fn get_dict<K, V>(
&self,
index: usize,
key_size: usize,
key_reader: KeyReader<K>,
val_reader: ValReader<V>,
) -> Result<HashMap<K, V>, TvmStackError>
where
K: Hash + Eq + Clone,
{
let boc = self.get_boc(index)?;
let cell = boc.single_root()?;
let mut parser = cell.parser();
Ok(parser.load_dict(key_size, key_reader, val_reader)?)
}
fn get<T>(
&self,
index: usize,
extract: fn(&TvmStackEntry, usize) -> Result<T, TvmStackError>,
) -> Result<T, TvmStackError> {
let maybe_elem = self.elements.get(index);
match maybe_elem {
None => Err(TvmStackError::InvalidTvmStackIndex {
index,
len: self.elements.len(),
}),
Some(e) => extract(e, index),
}
}
fn extract_string(e: &TvmStackEntry, index: usize) -> Result<String, TvmStackError> {
if let TvmStackEntry::Number { number } = e {
number
.number
.parse()
.map_err(|_| TvmStackError::StringConversion {
e: e.clone(),
index,
})
} else {
Err(TvmStackError::StringConversion {
e: e.clone(),
index,
})
}
}
fn extract_i32(e: &TvmStackEntry, index: usize) -> Result<i32, TvmStackError> {
if let TvmStackEntry::Number { number } = e {
number
.number
.parse()
.map_err(|_| TvmStackError::I32Conversion {
e: e.clone(),
index,
})
} else {
Err(TvmStackError::I32Conversion {
e: e.clone(),
index,
})
}
}
fn extract_i64(e: &TvmStackEntry, index: usize) -> Result<i64, TvmStackError> {
if let TvmStackEntry::Number { number } = e {
number
.number
.parse()
.map_err(|_| TvmStackError::I64Conversion {
e: e.clone(),
index,
})
} else {
Err(TvmStackError::I64Conversion {
e: e.clone(),
index,
})
}
}
fn extract_biguint(e: &TvmStackEntry, index: usize) -> Result<BigUint, TvmStackError> {
if let TvmStackEntry::Number { number } = e {
number
.number
.parse()
.map_err(|_| TvmStackError::BigUintConversion {
e: e.clone(),
index,
})
} else {
Err(TvmStackError::BigUintConversion {
e: e.clone(),
index,
})
}
}
fn extract_bigint(e: &TvmStackEntry, index: usize) -> Result<BigInt, TvmStackError> {
if let TvmStackEntry::Number { number } = e {
number
.number
.parse()
.map_err(|_| TvmStackError::BigIntConversion {
e: e.clone(),
index,
})
} else {
Err(TvmStackError::BigIntConversion {
e: e.clone(),
index,
})
}
}
fn extract_boc(e: &TvmStackEntry, index: usize) -> Result<BagOfCells, TvmStackError> {
match e {
TvmStackEntry::Slice { slice } => {
BagOfCells::parse(&slice.bytes).map_err(|_| TvmStackError::BoCConversion {
e: e.clone(),
index,
})
}
TvmStackEntry::Cell { cell } => {
BagOfCells::parse(&cell.bytes).map_err(|_| TvmStackError::BoCConversion {
e: e.clone(),
index,
})
}
_ => Err(TvmStackError::BoCConversion {
e: e.clone(),
index,
}),
}
}
}
impl Default for TvmStack {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use crate::tl::stack::{TvmNumber, TvmStack, TvmStackEntry};
const SERIAL: &str = r#"[{"@type":"tvm.stackEntryNumber","number":{"number":"100500"}}]"#;
#[test]
fn serialize_works() {
let mut stack = TvmStack::default();
stack.elements.push(TvmStackEntry::Number {
number: TvmNumber {
number: String::from("100500"),
},
});
let serial = serde_json::to_string(&stack).unwrap();
println!("{}", serial);
assert_eq!(serial.as_str(), SERIAL)
}
#[test]
fn deserialize_works() {
let stack: TvmStack = serde_json::from_str(SERIAL).unwrap();
assert_eq!(stack.elements.len(), 1);
assert_eq!(100500, stack.get_i32(0).unwrap());
}
}