use core::str::FromStr;
use rgb::popls::bp::Coinselect;
use rgb::{CellAddr, Outpoint, OwnedState, StateCalc};
use strict_types::StrictVal;
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Display, Default)]
#[display(lowercase)]
pub enum CoinselectStrategy {
#[default]
Aggregate,
SmallSize,
}
impl FromStr for CoinselectStrategy {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"aggregate" => Ok(CoinselectStrategy::Aggregate),
"smallsize" => Ok(CoinselectStrategy::SmallSize),
s => Err(s.to_string()),
}
}
}
impl Coinselect for CoinselectStrategy {
fn coinselect<'a>(
&mut self,
invoiced_state: &StrictVal,
calc: &mut StateCalc,
owned_state: impl IntoIterator<
Item = &'a OwnedState<Outpoint>,
IntoIter: DoubleEndedIterator<Item = &'a OwnedState<Outpoint>>,
>,
) -> Option<Vec<(CellAddr, Outpoint)>> {
let res = match self {
CoinselectStrategy::Aggregate => owned_state
.into_iter()
.take_while(|owned| {
if calc.is_satisfied(invoiced_state) {
return false;
}
calc.accumulate(&owned.assignment.data).is_ok()
})
.map(|owned| (owned.addr, owned.assignment.seal))
.collect(),
CoinselectStrategy::SmallSize => owned_state
.into_iter()
.rev()
.take_while(|owned| {
if calc.is_satisfied(invoiced_state) {
return false;
}
calc.accumulate(&owned.assignment.data).is_ok()
})
.map(|owned| (owned.addr, owned.assignment.seal))
.collect(),
};
if !calc.is_satisfied(invoiced_state) {
return None;
};
Some(res)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn display_from_str() {
assert_eq!(CoinselectStrategy::Aggregate.to_string(), "aggregate");
assert_eq!(CoinselectStrategy::SmallSize.to_string(), "smallsize");
assert_eq!(CoinselectStrategy::Aggregate, "aggregate".parse().unwrap());
assert_eq!(CoinselectStrategy::SmallSize, "smallsize".parse().unwrap());
}
}