#![allow(unused_braces)] use std::fmt;
use std::fmt::{Debug, Formatter};
use std::str::FromStr;
use amplify::ascii::AsciiString;
use amplify::confinement::{Confined, SmallString};
use amplify::IoError;
use baid58::Baid58ParseError;
use bp::dbc::LIB_NAME_BPCORE;
use bp::LIB_NAME_BITCOIN;
use strict_encoding::{InvalidIdent, StrictDeserialize, StrictDumb, StrictSerialize};
use strict_types::typelib::{LibBuilder, TranslateError};
use strict_types::typesys::SystemBuilder;
use strict_types::{typesys, Dependency, SemId, TypeLib, TypeLibId, TypeSystem};
pub const LIB_NAME_RGB_CONTRACT: &str = "RGBContract";
#[derive(Wrapper, WrapperMut, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Default, From)]
#[wrapper(Deref, Display, FromStr, MathOps)]
#[wrapper_mut(DerefMut, MathAssign)]
#[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 Timestamp(i32);
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Default)]
#[repr(u8)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB_CONTRACT, tags = repr, into_u8, try_from_u8)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub enum Precision {
Indivisible = 0,
Deci = 1,
Centi = 2,
Milli = 3,
DeciMilli = 4,
CentiMilli = 5,
Micro = 6,
DeciMicro = 7,
#[default]
CentiMicro = 8,
Nano = 9,
DeciNano = 10,
CentiNano = 11,
Pico = 12,
DeciPico = 13,
CentiPico = 14,
Femto = 15,
DeciFemto = 16,
CentiFemto = 17,
Atto = 18,
}
impl StrictSerialize for Precision {}
impl StrictDeserialize for Precision {}
#[derive(Wrapper, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, From)]
#[wrapper(Deref, Display)]
#[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(Confined<AsciiString, 1, 8>);
impl FromStr for Ticker {
type Err = InvalidIdent;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = AsciiString::from_ascii(s.as_bytes())?;
Self::try_from(s)
}
}
impl From<&'static str> for Ticker {
fn from(s: &'static str) -> Self { Self::from_str(s).expect("invalid ticker name") }
}
impl TryFrom<String> for Ticker {
type Error = InvalidIdent;
fn try_from(s: String) -> Result<Self, Self::Error> {
let s = AsciiString::from_ascii(s.as_bytes())?;
Self::try_from(s)
}
}
impl TryFrom<AsciiString> for Ticker {
type Error = InvalidIdent;
fn try_from(ascii: AsciiString) -> Result<Self, InvalidIdent> {
if ascii.is_empty() {
return Err(InvalidIdent::Empty);
}
if let Some(ch) = ascii
.as_slice()
.iter()
.copied()
.find(|ch| !ch.is_ascii_uppercase())
{
return Err(InvalidIdent::InvalidChar(ch));
}
let s = Confined::try_from(ascii)?;
Ok(Self(s))
}
}
impl Debug for Ticker {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_tuple("Ticker").field(&self.as_str()).finish()
}
}
#[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 ContractName(Confined<String, 1, 40>);
impl StrictDumb for ContractName {
fn strict_dumb() -> Self { Self(Confined::try_from(s!("Dumb contract name")).unwrap()) }
}
impl FromStr for ContractName {
type Err = InvalidIdent;
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 ContractName {
fn from(s: &'static str) -> Self { Self::from_str(s).expect("invalid ticker name") }
}
impl TryFrom<String> for ContractName {
type Error = InvalidIdent;
fn try_from(name: String) -> Result<Self, InvalidIdent> {
let s = Confined::try_from(name)?;
Ok(Self(s))
}
}
impl Debug for ContractName {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_tuple("ContractName").field(&self.as_str()).finish()
}
}
#[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 ContractDetails(Confined<String, 40, 255>);
impl StrictDumb for ContractDetails {
fn strict_dumb() -> Self {
Self(Confined::try_from(s!("Dumb long description which is stupid and so on...")).unwrap())
}
}
impl FromStr for ContractDetails {
type Err = InvalidIdent;
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 ContractDetails {
fn from(s: &'static str) -> Self { Self::from_str(s).expect("invalid ticker name") }
}
impl TryFrom<String> for ContractDetails {
type Error = InvalidIdent;
fn try_from(name: String) -> Result<Self, InvalidIdent> {
let s = Confined::try_from(name)?;
Ok(Self(s))
}
}
impl Debug for ContractDetails {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_tuple("ContractDetails")
.field(&self.as_str())
.finish()
}
}
#[derive(Clone, Eq, PartialEq, 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 Nominal {
ticker: Ticker,
name: ContractName,
details: Option<ContractDetails>,
precision: Precision,
}
impl StrictSerialize for Nominal {}
impl StrictDeserialize for Nominal {}
impl Nominal {
pub fn new(ticker: &'static str, name: &'static str, precision: Precision) -> Nominal {
Nominal {
ticker: Ticker::from(ticker),
name: ContractName::from(name),
details: None,
precision,
}
}
pub fn with(ticker: &str, name: &str, precision: Precision) -> Result<Nominal, InvalidIdent> {
Ok(Nominal {
ticker: Ticker::try_from(ticker.to_owned())?,
name: ContractName::try_from(name.to_owned())?,
details: None,
precision,
})
}
}
#[derive(Clone, Eq, PartialEq, Debug, Default)]
#[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 ContractText(SmallString);
impl StrictSerialize for ContractText {}
impl StrictDeserialize for ContractText {}
#[derive(Debug, From)]
enum Error {
#[from(std::io::Error)]
Io(IoError),
#[from]
Baid58(Baid58ParseError),
#[from]
Translate(TranslateError),
#[from]
Compile(typesys::Error),
#[from]
Link(Vec<typesys::Error>),
}
#[derive(Debug)]
pub struct StandardLib(TypeLib);
impl StandardLib {
pub fn new() -> Self {
fn builder() -> Result<TypeLib, Error> {
let bitcoin_id = TypeLibId::from_str(
"circus_report_jeep_2bj6eDer24ZBSVq6JgQW2BrARt6vx56vMWzF35J45gzY",
)?;
let bpcore_id = TypeLibId::from_str(
"harlem_null_puma_DxuLX8d9UiMyEJMRJivMFviK1B8t1QWyjywXuDC13iKR",
)?;
let imports = bset! {
Dependency::with(bitcoin_id, libname!(LIB_NAME_BITCOIN)),
Dependency::with(bpcore_id, libname!(LIB_NAME_BPCORE)),
};
LibBuilder::new(libname!(LIB_NAME_RGB_CONTRACT))
.process::<Timestamp>()?
.process::<Nominal>()?
.process::<ContractText>()?
.compile(imports)
.map_err(Error::from)
}
Self(builder().expect("error in standard RGBContract type library"))
}
pub fn type_lib(&self) -> TypeLib { self.0.clone() }
}
#[derive(Debug)]
pub struct StandardTypes(TypeSystem);
impl StandardTypes {
pub fn new() -> Self {
fn builder() -> Result<TypeSystem, Error> {
let lib = StandardLib::new().type_lib();
let sys = SystemBuilder::new().import(lib)?.finalize()?;
Ok(sys)
}
Self(builder().expect("error in standard RGBContract type system"))
}
pub fn type_system(&self) -> TypeSystem { self.0.clone() }
pub fn get(&self, name: &'static str) -> SemId {
self.0
.id_by_name(name)
.expect("type is absent in standard RGBContract type library")
}
}