use super::{Amount, Compilable, CompilationError, Compiled};
use crate::contract::compiler::InternalCompilerTag;
use bitcoin::Network;
use sapio_base::effects::EffectPath;
use sapio_base::effects::PathFragment;
pub use sapio_base::effects::{EffectDB, MapEffectDB};
use sapio_ctv_emulator_trait::CTVEmulator;
use std::convert::TryInto;
use std::collections::HashSet;
use std::sync::Arc;
pub struct Context {
available_funds: Amount,
emulator: Arc<dyn CTVEmulator>,
pub network: Network,
path: Arc<EffectPath>,
already_derived: HashSet<PathFragment>,
effects: Arc<MapEffectDB>,
}
impl Context {
pub fn new(
network: Network,
available_funds: Amount,
emulator: Arc<dyn CTVEmulator>,
path: EffectPath,
effects: Arc<MapEffectDB>,
) -> Self {
Context {
available_funds,
emulator,
network,
path: Arc::new(path),
already_derived: Default::default(),
effects,
}
}
pub unsafe fn get_effects_internal(&self) -> &Arc<MapEffectDB> {
&self.effects
}
pub(crate) fn get_effects(&self, _: InternalCompilerTag) -> &Arc<MapEffectDB> {
&self.effects
}
pub fn path(&self) -> &Arc<EffectPath> {
&self.path
}
pub fn derive_str<'a>(&mut self, path: Arc<String>) -> Result<Self, CompilationError> {
let p: PathFragment = path.try_into()?;
if matches!(p, PathFragment::Named(_)) {
self.derive(p)
} else {
Err(CompilationError::InvalidPathName)
}
}
pub fn derive_num<T: Into<u64>>(&mut self, path: T) -> Result<Self, CompilationError> {
self.derive(PathFragment::Branch(path.into()))
}
pub(crate) fn derive(&mut self, path: PathFragment) -> Result<Self, CompilationError> {
if self.already_derived.contains(&path) {
Err(CompilationError::ContexPathAlreadyDerived)
} else {
self.already_derived.insert(path.clone());
let new_path = EffectPath::push(Some(self.path.clone()), path);
Ok(Context {
available_funds: self.available_funds,
emulator: self.emulator.clone(),
path: new_path,
network: self.network,
already_derived: Default::default(),
effects: self.effects.clone(),
})
}
}
pub(crate) fn internal_clone(&self, _i: InternalCompilerTag) -> Self {
Context {
available_funds: self.available_funds,
emulator: self.emulator.clone(),
path: self.path.clone(),
network: self.network,
already_derived: self.already_derived.clone(),
effects: self.effects.clone(),
}
}
pub fn funds(&self) -> Amount {
self.available_funds
}
pub fn ctv_emulator(
&self,
b: bitcoin::hashes::sha256::Hash,
) -> Result<sapio_base::Clause, CompilationError> {
Ok(self.emulator.get_signer_for(b)?)
}
pub fn compile<A: Compilable>(self, a: A) -> Result<Compiled, CompilationError> {
a.compile(self)
}
pub fn with_amount(self, amount: Amount) -> Result<Self, CompilationError> {
if self.available_funds < amount {
Err(CompilationError::OutOfFunds)
} else {
Ok(Context {
available_funds: amount,
emulator: self.emulator.clone(),
path: self.path.clone(),
network: self.network,
already_derived: self.already_derived.clone(),
effects: self.effects.clone(),
})
}
}
pub fn spend_amount(mut self, amount: Amount) -> Result<Self, CompilationError> {
if self.available_funds < amount {
Err(CompilationError::OutOfFunds)
} else {
self.available_funds -= amount;
Ok(self)
}
}
pub fn add_amount(mut self, amount: Amount) -> Self {
self.available_funds += amount;
self
}
pub fn template(self) -> crate::template::Builder {
crate::template::Builder::new(self)
}
}