use solana_program::msg;
use crate::{
error::RuleSetError,
state::{try_from_bytes, RuleResult},
state::{
v2::{Constraint, ConstraintType, Operator, Str32, HEADER_SECTION, U64_BYTES},
Header,
},
};
pub struct Amount<'a> {
pub amount: &'a u64,
pub operator: &'a u64,
pub field: &'a Str32,
}
impl<'a> Amount<'a> {
pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, RuleSetError> {
let amount = try_from_bytes::<u64>(0, U64_BYTES, bytes)?;
let mut cursor = U64_BYTES;
let operator = try_from_bytes::<u64>(cursor, U64_BYTES, bytes)?;
cursor += U64_BYTES;
let field = try_from_bytes::<Str32>(cursor, Str32::SIZE, bytes)?;
Ok(Self {
amount,
operator,
field,
})
}
pub fn serialize(
field: String,
operator: Operator,
amount: u64,
) -> Result<Vec<u8>, RuleSetError> {
let length = (U64_BYTES + U64_BYTES + Str32::SIZE) as u32;
let mut data = Vec::with_capacity(HEADER_SECTION + length as usize);
Header::serialize(ConstraintType::Amount, length, &mut data);
data.extend(u64::to_le_bytes(amount));
data.extend(u64::to_le_bytes(operator as u64));
let mut field_bytes = [0u8; Str32::SIZE];
field_bytes[..field.len()].copy_from_slice(field.as_bytes());
data.extend(field_bytes);
Ok(data)
}
}
impl<'a> Constraint<'a> for Amount<'a> {
fn constraint_type(&self) -> ConstraintType {
ConstraintType::Amount
}
fn validate(
&self,
_accounts: &std::collections::HashMap<
solana_program::pubkey::Pubkey,
&solana_program::account_info::AccountInfo,
>,
payload: &crate::payload::Payload,
_update_rule_state: bool,
_rule_set_state_pda: &Option<&solana_program::account_info::AccountInfo>,
_rule_authority: &Option<&solana_program::account_info::AccountInfo>,
) -> RuleResult {
msg!("Validating Amount");
let condition_type = self.constraint_type();
if let Some(payload_amount) = &payload.get_amount(&self.field.to_string()) {
let operator_fn = match Operator::try_from(*self.operator) {
Ok(Operator::Lt) => PartialOrd::lt,
Ok(Operator::LtEq) => PartialOrd::le,
Ok(Operator::Eq) => PartialEq::eq,
Ok(Operator::Gt) => PartialOrd::gt,
Ok(Operator::GtEq) => PartialOrd::ge,
Err(_) => return RuleResult::Failure(condition_type.to_error()),
};
if operator_fn(payload_amount, self.amount) {
RuleResult::Success(condition_type.to_error())
} else {
RuleResult::Failure(condition_type.to_error())
}
} else {
RuleResult::Error(RuleSetError::MissingPayloadValue.into())
}
}
}