use chrono::{DateTime, Utc};
use impl_tools::autoimpl;
use num_bigint::BigUint;
use tlb::{
BitPack, BitReader, BitReaderExt, BitUnpack, BitWriter, BitWriterExt, Cell, CellBuilder,
CellBuilderError, CellDeserialize, CellParser, CellParserError, CellSerialize, Either, NBits,
Ref, Same,
};
use crate::{Grams, MsgAddress, UnixTimestamp};
#[derive(Debug, Clone)]
pub struct Message<T = Cell, IC = Cell, ID = Cell, IL = Cell> {
pub info: CommonMsgInfo,
pub init: Option<StateInit<IC, ID, IL>>,
pub body: T,
}
impl<T, IC, ID, IL> CellSerialize for Message<T, IC, ID, IL>
where
T: CellSerialize,
IC: CellSerialize,
ID: CellSerialize,
IL: CellSerialize,
{
fn store(&self, builder: &mut CellBuilder) -> Result<(), CellBuilderError> {
builder
.pack(&self.info)?
.store_as::<_, Option<Either<(), Ref>>>(self.init.as_ref().map(Some))?
.store_as::<_, Ref>(&self.body)?;
Ok(())
}
}
impl<'de, T, IC, ID, IL> CellDeserialize<'de> for Message<T, IC, ID, IL>
where
T: CellDeserialize<'de>,
IC: CellDeserialize<'de>,
ID: CellDeserialize<'de>,
IL: CellDeserialize<'de>,
{
fn parse(parser: &mut CellParser<'de>) -> Result<Self, CellParserError<'de>> {
Ok(Self {
info: parser.unpack()?,
init: parser
.parse_as::<_, Option<Either<Same, Ref>>>()?
.map(Either::into_inner),
body: parser
.parse_as::<Either<T, T>, Either<Same, Ref>>()?
.into_inner(),
})
}
}
#[derive(Debug, Clone)]
pub enum CommonMsgInfo {
Internal(InternalMsgInfo),
ExternalIn(ExternalInMsgInfo),
ExternalOut(ExternalOutMsgInfo),
}
impl BitPack for CommonMsgInfo {
fn pack<W>(&self, mut writer: W) -> Result<(), W::Error>
where
W: BitWriter,
{
match self {
Self::Internal(msg) => writer
.pack(false)?
.pack(msg)?,
Self::ExternalIn(msg) => writer
.pack_as::<_, NBits<2>>(0b10)?
.pack(msg)?,
Self::ExternalOut(msg) => writer
.pack_as::<_, NBits<2>>(0b11)?
.pack(msg)?,
};
Ok(())
}
}
impl BitUnpack for CommonMsgInfo {
fn unpack<R>(mut reader: R) -> Result<Self, R::Error>
where
R: BitReader,
{
match reader.unpack()? {
false => Ok(Self::Internal(reader.unpack()?)),
true => match reader.unpack()? {
false => Ok(Self::ExternalIn(reader.unpack()?)),
true => Ok(Self::ExternalOut(reader.unpack()?)),
},
}
}
}
#[derive(Debug, Clone)]
pub struct InternalMsgInfo {
pub ihr_disabled: bool,
pub bounce: bool,
pub bounced: bool,
pub src: MsgAddress,
pub dst: MsgAddress,
pub value: CurrencyCollection,
pub ihr_fee: BigUint,
pub fwd_fee: BigUint,
pub created_lt: u64,
pub created_at: DateTime<Utc>,
}
impl BitPack for InternalMsgInfo {
fn pack<W>(&self, mut writer: W) -> Result<(), W::Error>
where
W: BitWriter,
{
writer
.pack(self.ihr_disabled)?
.pack(self.bounce)?
.pack(self.bounced)?
.pack(self.src)?
.pack(self.dst)?
.pack(&self.value)?
.pack_as::<_, &Grams>(&self.ihr_fee)?
.pack_as::<_, &Grams>(&self.fwd_fee)?
.pack(self.created_lt)?
.pack_as::<_, UnixTimestamp>(self.created_at)?;
Ok(())
}
}
impl BitUnpack for InternalMsgInfo {
fn unpack<R>(mut reader: R) -> Result<Self, R::Error>
where
R: BitReader,
{
Ok(Self {
ihr_disabled: reader.unpack()?,
bounce: reader.unpack()?,
bounced: reader.unpack()?,
src: reader.unpack()?,
dst: reader.unpack()?,
value: reader.unpack()?,
ihr_fee: reader.unpack_as::<_, Grams>()?,
fwd_fee: reader.unpack_as::<_, Grams>()?,
created_lt: reader.unpack()?,
created_at: reader.unpack_as::<_, UnixTimestamp>()?,
})
}
}
#[derive(Debug, Clone)]
pub struct ExternalInMsgInfo {
pub src: MsgAddress,
pub dst: MsgAddress,
pub import_fee: BigUint,
}
impl BitPack for ExternalInMsgInfo {
fn pack<W>(&self, mut writer: W) -> Result<(), W::Error>
where
W: BitWriter,
{
writer
.pack(self.src)?
.pack(self.dst)?
.pack_as::<_, &Grams>(&self.import_fee)?;
Ok(())
}
}
impl BitUnpack for ExternalInMsgInfo {
fn unpack<R>(mut reader: R) -> Result<Self, R::Error>
where
R: BitReader,
{
Ok(Self {
src: reader.unpack()?,
dst: reader.unpack()?,
import_fee: reader.unpack_as::<_, Grams>()?,
})
}
}
#[derive(Debug, Clone)]
pub struct ExternalOutMsgInfo {
pub src: MsgAddress,
pub dst: MsgAddress,
pub created_lt: u64,
pub created_at: DateTime<Utc>,
}
impl BitPack for ExternalOutMsgInfo {
fn pack<W>(&self, mut writer: W) -> Result<(), W::Error>
where
W: BitWriter,
{
writer
.pack(self.src)?
.pack(self.dst)?
.pack(self.created_lt)?
.pack_as::<_, UnixTimestamp>(self.created_at)?;
Ok(())
}
}
impl BitUnpack for ExternalOutMsgInfo {
fn unpack<R>(mut reader: R) -> Result<Self, R::Error>
where
R: BitReader,
{
Ok(Self {
src: reader.unpack()?,
dst: reader.unpack()?,
created_lt: reader.unpack()?,
created_at: reader.unpack_as::<_, UnixTimestamp>()?,
})
}
}
#[derive(Debug, Clone, Copy)]
pub struct TickTock {
tick: bool,
tock: bool,
}
impl BitPack for TickTock {
fn pack<W>(&self, mut writer: W) -> Result<(), W::Error>
where
W: BitWriter,
{
writer.pack(self.tick)?.pack(self.tock)?;
Ok(())
}
}
impl BitUnpack for TickTock {
fn unpack<R>(mut reader: R) -> Result<Self, R::Error>
where
R: BitReader,
{
Ok(Self {
tick: reader.unpack()?,
tock: reader.unpack()?,
})
}
}
#[derive(Debug, Clone)]
#[autoimpl(Default)]
pub struct StateInit<C = Cell, D = Cell, L = Cell> {
pub split_depth: Option<u8>,
pub special: Option<TickTock>,
pub code: Option<C>,
pub data: Option<D>,
pub library: Option<L>,
}
impl<C, D, L> CellSerialize for StateInit<C, D, L>
where
C: CellSerialize,
D: CellSerialize,
L: CellSerialize,
{
fn store(&self, builder: &mut CellBuilder) -> Result<(), CellBuilderError> {
builder
.pack_as::<_, Option<NBits<5>>>(self.split_depth)?
.pack(self.special)?
.store_as::<_, Option<Ref>>(self.code.as_ref())?
.store_as::<_, Option<Ref>>(self.data.as_ref())?
.store_as::<_, Option<Ref>>(self.library.as_ref())?;
Ok(())
}
}
impl<'de, C, D, L> CellDeserialize<'de> for StateInit<C, D, L>
where
C: CellDeserialize<'de>,
D: CellDeserialize<'de>,
L: CellDeserialize<'de>,
{
fn parse(parser: &mut CellParser<'de>) -> Result<Self, CellParserError<'de>> {
Ok(Self {
split_depth: parser.unpack_as::<_, Option<NBits<5>>>()?,
special: parser.unpack()?,
code: parser.parse_as::<_, Option<Ref>>()?,
data: parser.parse_as::<_, Option<Ref>>()?,
library: parser.parse_as::<_, Option<Ref>>()?,
})
}
}
#[derive(Debug, Clone)]
pub struct CurrencyCollection {
pub grams: BigUint,
pub other: ExtraCurrencyCollection,
}
impl BitPack for CurrencyCollection {
fn pack<W>(&self, mut writer: W) -> Result<(), W::Error>
where
W: BitWriter,
{
writer
.pack_as::<_, &Grams>(&self.grams)?
.pack(&self.other)?;
Ok(())
}
}
impl BitUnpack for CurrencyCollection {
fn unpack<R>(mut reader: R) -> Result<Self, R::Error>
where
R: BitReader,
{
Ok(Self {
grams: reader.unpack_as::<_, Grams>()?,
other: reader.unpack()?,
})
}
}
#[derive(Debug, Clone)]
pub struct ExtraCurrencyCollection;
impl BitPack for ExtraCurrencyCollection {
fn pack<W>(&self, writer: W) -> Result<(), W::Error>
where
W: BitWriter,
{
false.pack(writer)
}
}
impl BitUnpack for ExtraCurrencyCollection {
fn unpack<R>(_reader: R) -> Result<Self, R::Error>
where
R: BitReader,
{
Ok(Self)
}
}