use amplify::confinement::{LargeOrdMap, LargeVec, SmallVec};
use bp::Outpoint;
use rgb::{
AssignmentType, AttachId, ContractState, FungibleOutput, MediaType, RevealedAttach,
RevealedData, SealWitness,
};
use strict_encoding::TypeName;
use strict_types::typify::TypedVal;
use strict_types::{decode, StrictVal};
use crate::interface::IfaceImpl;
use crate::LIB_NAME_RGB_STD;
#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
#[display(doc_comments)]
pub enum ContractError {
TypeNameUnknown(TypeName),
#[from]
#[display(inner)]
Reify(decode::Error),
}
#[derive(Clone, Eq, PartialEq, Debug, Display, From)]
#[display(inner)]
pub enum TypedState {
#[display("")]
Void,
#[from]
Amount(u64),
#[from]
Data(RevealedData),
#[from]
Attachment(AttachedState),
}
#[derive(Clone, Eq, PartialEq, Debug, Display)]
#[display("{id}:{media_type}")]
pub struct AttachedState {
pub id: AttachId,
pub media_type: MediaType,
}
impl From<RevealedAttach> for AttachedState {
fn from(attach: RevealedAttach) -> Self {
AttachedState {
id: attach.id,
media_type: attach.media_type,
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct FungibleAllocation {
pub owner: Outpoint,
pub witness: SealWitness,
pub value: u64,
}
impl From<FungibleOutput> for FungibleAllocation {
fn from(out: FungibleOutput) -> Self { Self::from(&out) }
}
impl From<&FungibleOutput> for FungibleAllocation {
fn from(out: &FungibleOutput) -> Self {
FungibleAllocation {
owner: out.seal,
witness: out.witness,
value: out.state.value.as_u64(),
}
}
}
#[derive(Clone, Eq, PartialEq, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB_STD)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct ContractIface {
pub state: ContractState,
pub iface: IfaceImpl,
}
impl ContractIface {
pub fn global(&self, name: impl Into<TypeName>) -> Result<SmallVec<StrictVal>, ContractError> {
let name = name.into();
let type_system = &self.state.schema.type_system;
let type_id = self
.iface
.global_type(&name)
.ok_or(ContractError::TypeNameUnknown(name))?;
let type_schema = self
.state
.schema
.global_types
.get(&type_id)
.expect("schema doesn't match interface");
let state = unsafe { self.state.global_unchecked(type_id) };
let state = state
.into_iter()
.map(|revealed| {
type_system
.strict_deserialize_type(type_schema.sem_id, revealed.as_ref())
.map(TypedVal::unbox)
})
.take(type_schema.max_items as usize)
.collect::<Result<Vec<_>, _>>()?;
Ok(SmallVec::try_from_iter(state).expect("same or smaller collection size"))
}
pub fn fungible(
&self,
name: impl Into<TypeName>,
) -> Result<LargeVec<FungibleAllocation>, ContractError> {
let name = name.into();
let type_id = self
.iface
.assignments_type(&name)
.ok_or(ContractError::TypeNameUnknown(name))?;
let state = self
.state
.fungibles()
.iter()
.filter(|outp| outp.opout.ty == type_id)
.map(FungibleAllocation::from);
Ok(LargeVec::try_from_iter(state).expect("same or smaller collection size"))
}
pub fn outpoint(
&self,
_outpoint: Outpoint,
) -> LargeOrdMap<AssignmentType, LargeVec<TypedState>> {
todo!()
}
}