#![allow(unused_braces)]
use std::collections::BTreeMap;
use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::str::FromStr;
use amplify::ascii::AsciiString;
use amplify::confinement::{
Confined, NonEmptyString, NonEmptyVec, SmallBlob, SmallOrdSet, SmallString, U8,
};
use amplify::Bytes32;
use invoice::{Precision, TokenIndex};
use strict_encoding::stl::{Alpha, AlphaNum, AsciiPrintable};
use strict_encoding::{
DefaultBasedStrictDumb, InvalidRString, RString, StrictDecode, StrictDeserialize, StrictDumb,
StrictEncode, StrictSerialize, StrictType, TypedWrite,
};
use strict_types::StrictVal;
use super::{MediaType, ProofOfReserves, LIB_NAME_RGB_CONTRACT};
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
#[derive(StrictType, StrictEncode, StrictDecode, StrictDumb)]
#[strict_type(lib = LIB_NAME_RGB_CONTRACT)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct BurnMeta {
pub burn_proofs: SmallOrdSet<ProofOfReserves>,
}
impl StrictSerialize for BurnMeta {}
impl StrictDeserialize for BurnMeta {}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
#[derive(StrictType, StrictEncode, StrictDecode, StrictDumb)]
#[strict_type(lib = LIB_NAME_RGB_CONTRACT)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct IssueMeta {
pub reserves: SmallOrdSet<ProofOfReserves>,
}
impl StrictSerialize for IssueMeta {}
impl StrictDeserialize for IssueMeta {}
#[derive(Wrapper, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, From)]
#[wrapper(Deref, Display, FromStr)]
#[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB_CONTRACT, dumb = { Article::from("DUMB") })]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
pub struct Article(RString<Alpha, AlphaNum, 1, 32>);
impl_ident_type!(Article);
impl_ident_subtype!(Article);
#[derive(Wrapper, Clone, Ord, PartialOrd, Eq, From)]
#[wrapper(Deref, Display, FromStr)]
#[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB_CONTRACT, dumb = { Ticker::from("DUMB") })]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
pub struct Ticker(RString<Alpha, AlphaNum, 1, 8>);
impl PartialEq for Ticker {
fn eq(&self, other: &Self) -> bool {
self.as_str()
.to_uppercase()
.eq(&other.as_str().to_uppercase())
}
}
impl Hash for Ticker {
fn hash<H: Hasher>(&self, state: &mut H) { self.as_str().to_uppercase().hash(state) }
}
impl_ident_type!(Ticker);
impl_ident_subtype!(Ticker);
#[derive(Wrapper, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, From)]
#[wrapper(Deref, Display, FromStr)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB_CONTRACT)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
pub struct Name(RString<AsciiPrintable, AsciiPrintable, 1, 40>);
impl StrictSerialize for Name {}
impl StrictDeserialize for Name {}
impl_ident_type!(Name);
impl_ident_subtype!(Name);
impl Name {
pub fn from_strict_val_unchecked(value: &StrictVal) -> Self {
Name::from_str(&value.unwrap_string()).unwrap()
}
}
#[derive(Wrapper, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, From)]
#[wrapper(Deref, Display)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB_CONTRACT)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
pub struct Details(NonEmptyString<U8>);
impl StrictSerialize for Details {}
impl StrictDeserialize for Details {}
impl AsRef<str> for Details {
#[inline]
fn as_ref(&self) -> &str { self.0.as_str() }
}
impl Details {
pub fn from_strict_val_unchecked(value: &StrictVal) -> Self {
Details::from_str(&value.unwrap_string()).unwrap()
}
}
impl StrictDumb for Details {
fn strict_dumb() -> Self {
Self(Confined::try_from(s!("Dumb long description which is stupid and so on...")).unwrap())
}
}
impl FromStr for Details {
type Err = InvalidRString;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = Confined::try_from_iter(s.chars())?;
Ok(Self(s))
}
}
impl From<&'static str> for Details {
fn from(s: &'static str) -> Self { Self::from_str(s).expect("invalid details") }
}
impl TryFrom<String> for Details {
type Error = InvalidRString;
fn try_from(name: String) -> Result<Self, InvalidRString> {
let s = Confined::try_from(name)?;
Ok(Self(s))
}
}
impl Debug for Details {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_tuple("ContractDetails")
.field(&self.as_str())
.finish()
}
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
#[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB_CONTRACT)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct AssetSpec {
pub ticker: Ticker,
pub name: Name,
pub details: Option<Details>,
pub precision: Precision,
}
impl StrictSerialize for AssetSpec {}
impl StrictDeserialize for AssetSpec {}
impl AssetSpec {
pub fn new(ticker: &'static str, name: &'static str, precision: Precision) -> AssetSpec {
AssetSpec {
ticker: Ticker::from(ticker),
name: Name::from(name),
details: None,
precision,
}
}
pub fn with(
ticker: &str,
name: &str,
precision: Precision,
details: Option<&str>,
) -> Result<AssetSpec, InvalidRString> {
Ok(AssetSpec {
ticker: Ticker::try_from(ticker.to_owned())?,
name: Name::try_from(name.to_owned())?,
details: details.map(Details::from_str).transpose()?,
precision,
})
}
pub fn from_strict_val_unchecked(value: &StrictVal) -> Self {
let ticker = value.unwrap_struct("ticker").unwrap_string();
let name = value.unwrap_struct("name").unwrap_string();
let details = value
.unwrap_struct("details")
.unwrap_option()
.map(StrictVal::unwrap_string);
let precision = value.unwrap_struct("precision").unwrap_enum();
Self {
ticker: Ticker::from_str(&ticker).expect("invalid asset ticker"),
name: Name::from_str(&name).expect("invalid asset name"),
details: details
.as_deref()
.map(Details::from_str)
.transpose()
.expect("invalid asset details"),
precision,
}
}
pub fn ticker(&self) -> &str { self.ticker.as_str() }
pub fn name(&self) -> &str { self.name.as_str() }
pub fn details(&self) -> Option<&str> { self.details.as_ref().map(|d| d.as_str()) }
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
#[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB_CONTRACT)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct ContractSpec {
pub article: Option<Article>,
pub name: Name,
pub details: Option<Details>,
pub precision: Precision,
}
impl StrictSerialize for ContractSpec {}
impl StrictDeserialize for ContractSpec {}
impl ContractSpec {
pub fn new(name: &'static str, precision: Precision) -> ContractSpec {
ContractSpec {
article: None,
name: Name::from(name),
details: None,
precision,
}
}
pub fn with(
article: &str,
name: &str,
precision: Precision,
details: Option<&str>,
) -> Result<ContractSpec, InvalidRString> {
Ok(ContractSpec {
article: Some(Article::try_from(article.to_owned())?),
name: Name::try_from(name.to_owned())?,
details: details.map(Details::from_str).transpose()?,
precision,
})
}
pub fn from_strict_val_unchecked(value: &StrictVal) -> Self {
let article = value.unwrap_struct("article").unwrap_option();
let name = value.unwrap_struct("name").unwrap_string();
let details = value
.unwrap_struct("details")
.unwrap_option()
.map(StrictVal::unwrap_string);
let precision = value.unwrap_struct("precision").unwrap_enum();
Self {
article: article.map(|val| {
Article::from_str(&val.unwrap_string()).expect("invalid contract article")
}),
name: Name::from_str(&name).expect("invalid contract name"),
details: details
.as_deref()
.map(Details::from_str)
.transpose()
.expect("invalid contract details"),
precision,
}
}
pub fn article(&self) -> Option<&str> { self.article.as_ref().map(|a| a.as_str()) }
pub fn name(&self) -> &str { self.name.as_str() }
pub fn details(&self) -> Option<&str> { self.details.as_ref().map(|d| d.as_str()) }
}
#[derive(Clone, Eq, PartialEq, Hash, Debug, Display, Default)]
#[display(inner)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB_CONTRACT)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
pub struct RicardianContract(SmallString);
impl DefaultBasedStrictDumb for RicardianContract {}
impl StrictSerialize for RicardianContract {}
impl StrictDeserialize for RicardianContract {}
impl AsRef<str> for RicardianContract {
#[inline]
fn as_ref(&self) -> &str { self.0.as_str() }
}
impl FromStr for RicardianContract {
type Err = InvalidRString;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = Confined::try_from_iter(s.chars())?;
Ok(Self(s))
}
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB_CONTRACT)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct Attachment {
#[strict_type(rename = "type")]
#[cfg_attr(feature = "serde", serde(rename = "type"))]
pub ty: MediaType,
pub digest: Bytes32,
}
impl StrictSerialize for Attachment {}
impl StrictDeserialize for Attachment {}
impl Attachment {
pub fn from_strict_val_unchecked(value: &StrictVal) -> Self {
let ty = MediaType::from_strict_val_unchecked(value.unwrap_struct("type"));
let digest = value
.unwrap_struct("digest")
.unwrap_bytes()
.try_into()
.expect("invalid digest");
Self { ty, digest }
}
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
#[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB_CONTRACT)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct ContractTerms {
pub text: RicardianContract,
pub media: Option<Attachment>,
}
impl StrictSerialize for ContractTerms {}
impl StrictDeserialize for ContractTerms {}
impl ContractTerms {
pub fn from_strict_val_unchecked(value: &StrictVal) -> Self {
let text = RicardianContract::from_str(&value.unwrap_struct("text").unwrap_string())
.expect("invalid text");
let media = value
.unwrap_struct("media")
.unwrap_option()
.map(Attachment::from_strict_val_unchecked);
Self { text, media }
}
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB_CONTRACT)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct EmbeddedMedia {
#[strict_type(rename = "type")]
#[cfg_attr(feature = "serde", serde(rename = "type"))]
pub ty: MediaType,
pub data: SmallBlob,
}
impl EmbeddedMedia {
pub fn from_strict_val_unchecked(value: &StrictVal) -> Self {
let ty = MediaType::from_strict_val_unchecked(value.unwrap_struct("type"));
let data = SmallBlob::from_iter_checked(
value.unwrap_struct("data").unwrap_bytes().iter().copied(),
);
Self { ty, data }
}
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB_CONTRACT, dumb = { AttachmentType::with(0, "dumb") })]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct AttachmentType {
pub id: u8,
pub name: AttachmentName,
}
impl AttachmentType {
pub fn with(id: u8, name: &'static str) -> AttachmentType {
AttachmentType {
id,
name: AttachmentName::from(name),
}
}
}
#[derive(Wrapper, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, From)]
#[wrapper(Deref, Display)]
#[derive(StrictType, StrictDumb, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB_CONTRACT, dumb = { AttachmentName::from("dumb") })]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
pub struct AttachmentName(Confined<AsciiString, 1, 20>);
impl StrictEncode for AttachmentName {
fn strict_encode<W: TypedWrite>(&self, writer: W) -> std::io::Result<W> {
let iter = self
.0
.as_bytes()
.iter()
.map(|c| AsciiPrintable::try_from(*c).unwrap());
writer
.write_newtype::<Self>(&NonEmptyVec::<AsciiPrintable, 20>::try_from_iter(iter).unwrap())
}
}
impl FromStr for AttachmentName {
type Err = InvalidRString;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = AsciiString::from_ascii(s.as_bytes())?;
let s = Confined::try_from_iter(s.chars())?;
Ok(Self(s))
}
}
impl From<&'static str> for AttachmentName {
fn from(s: &'static str) -> Self { Self::from_str(s).expect("invalid attachment name") }
}
impl TryFrom<String> for AttachmentName {
type Error = InvalidRString;
fn try_from(name: String) -> Result<Self, InvalidRString> {
let name = AsciiString::from_ascii(name.as_bytes())?;
let s = Confined::try_from(name)?;
Ok(Self(s))
}
}
impl Debug for AttachmentName {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_tuple("AttachmentName")
.field(&self.as_str())
.finish()
}
}
#[derive(Clone, Eq, PartialEq, Hash, Debug, Default)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB_CONTRACT)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct TokenData {
pub index: TokenIndex,
pub ticker: Option<Ticker>,
pub name: Option<Name>,
pub details: Option<Details>,
pub preview: Option<EmbeddedMedia>,
pub media: Option<Attachment>,
pub attachments: Confined<BTreeMap<u8, Attachment>, 0, 20>,
pub reserves: Option<ProofOfReserves>,
}
impl DefaultBasedStrictDumb for TokenData {}
impl StrictSerialize for TokenData {}
impl StrictDeserialize for TokenData {}
impl TokenData {
pub fn from_strict_val_unchecked(value: &StrictVal) -> Self {
let index = TokenIndex::from(
value
.unwrap_struct("index")
.unwrap_num()
.unwrap_uint::<u32>(),
);
let ticker = value
.unwrap_struct("ticker")
.unwrap_option()
.map(|x| Ticker::from_str(&x.unwrap_string()).expect("invalid uda ticker"));
let name = value
.unwrap_struct("name")
.unwrap_option()
.map(|x| Name::from_str(&x.unwrap_string()).expect("invalid uda name"));
let details = value
.unwrap_struct("details")
.unwrap_option()
.map(|x| Details::from_str(&x.unwrap_string()).expect("invalid uda details"));
let preview = value
.unwrap_struct("preview")
.unwrap_option()
.map(EmbeddedMedia::from_strict_val_unchecked);
let media = value
.unwrap_struct("media")
.unwrap_option()
.map(Attachment::from_strict_val_unchecked);
let attachments = if let StrictVal::Map(list) = value.unwrap_struct("attachments") {
Confined::from_iter_checked(
list.iter()
.map(|(k, v)| (k.unwrap_uint(), Attachment::from_strict_val_unchecked(v))),
)
} else {
Confined::default()
};
let reserves = value
.unwrap_struct("reserves")
.unwrap_option()
.map(ProofOfReserves::from_strict_val_unchecked);
Self {
index,
ticker,
name,
details,
preview,
media,
attachments,
reserves,
}
}
}
#[derive(Wrapper, Clone, Eq, PartialEq, Hash, From)]
#[wrapper(Deref, Display, FromStr)]
#[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB_CONTRACT)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
pub struct RejectListUrl(RString<AsciiPrintable, AsciiPrintable, 1, 8000>);
impl StrictSerialize for RejectListUrl {}
impl StrictDeserialize for RejectListUrl {}
impl_ident_type!(RejectListUrl);
impl_ident_subtype!(RejectListUrl);
impl RejectListUrl {
pub fn from_strict_val_unchecked(value: &StrictVal) -> Self {
RejectListUrl::from_str(&value.unwrap_string()).unwrap()
}
}