use std::collections::{BTreeSet, HashSet};
use amplify::confinement::{LargeOrdMap, LargeVec, SmallVec};
use bp::Outpoint;
use rgb::{
AssignmentType, AttachId, ContractId, ContractState, FungibleOutput, MediaType, RevealedAttach,
RevealedData, SealWitness,
};
use strict_encoding::FieldName;
use strict_types::typify::TypedVal;
use strict_types::{decode, StrictVal};
use crate::interface::IfaceImpl;
#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
#[display(doc_comments)]
pub enum ContractError {
FieldNameUnknown(FieldName),
#[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(),
}
}
}
pub trait OutpointFilter {
fn include_outpoint(&self, outpoint: Outpoint) -> bool;
}
impl OutpointFilter for Option<&[Outpoint]> {
fn include_outpoint(&self, outpoint: Outpoint) -> bool {
self.map(|filter| filter.include_outpoint(outpoint))
.unwrap_or(true)
}
}
impl OutpointFilter for &[Outpoint] {
fn include_outpoint(&self, outpoint: Outpoint) -> bool { self.contains(&outpoint) }
}
impl OutpointFilter for Vec<Outpoint> {
fn include_outpoint(&self, outpoint: Outpoint) -> bool { self.contains(&outpoint) }
}
impl OutpointFilter for HashSet<Outpoint> {
fn include_outpoint(&self, outpoint: Outpoint) -> bool { self.contains(&outpoint) }
}
impl OutpointFilter for BTreeSet<Outpoint> {
fn include_outpoint(&self, outpoint: Outpoint) -> bool { self.contains(&outpoint) }
}
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct ContractIface {
pub state: ContractState,
pub iface: IfaceImpl,
}
impl ContractIface {
pub fn contract_id(&self) -> ContractId { self.state.contract_id() }
pub fn global(&self, name: impl Into<FieldName>) -> 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::FieldNameUnknown(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<FieldName>,
filter: &impl OutpointFilter,
) -> Result<LargeVec<FungibleAllocation>, ContractError> {
let name = name.into();
let type_id = self
.iface
.assignments_type(&name)
.ok_or(ContractError::FieldNameUnknown(name))?;
let state = self
.state
.fungibles()
.iter()
.filter(|outp| outp.opout.ty == type_id)
.filter(|outp| filter.include_outpoint(outp.seal))
.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!()
}
}