use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
use solana_program::{
clock::UnixTimestamp,
info,
program_error::ProgramError,
program_pack::{Pack, Sealed},
};
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema, PartialEq)]
pub struct AcceptanceCriteria {
pub tokens_required: u64,
pub deadline: UnixTimestamp,
}
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema, PartialEq)]
pub enum FeatureProposal {
Uninitialized,
Pending(AcceptanceCriteria),
Accepted {
#[allow(dead_code)] tokens_upon_acceptance: u64,
},
Expired,
}
impl Sealed for FeatureProposal {}
impl Pack for FeatureProposal {
const LEN: usize = 17;
fn pack_into_slice(&self, dst: &mut [u8]) {
let data = self.try_to_vec().unwrap();
dst[..data.len()].copy_from_slice(&data);
}
fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
let mut mut_src: &[u8] = src;
Self::deserialize(&mut mut_src).map_err(|err| {
info!(&format!(
"Error: failed to deserialize feature proposal account: {}",
err
));
ProgramError::InvalidAccountData
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::borsh_utils;
#[test]
fn test_get_packed_len() {
assert_eq!(
FeatureProposal::get_packed_len(),
borsh_utils::get_packed_len::<FeatureProposal>()
);
}
#[test]
fn test_serialize_bytes() {
assert_eq!(FeatureProposal::Expired.try_to_vec().unwrap(), vec![3]);
assert_eq!(
FeatureProposal::Pending(AcceptanceCriteria {
tokens_required: 0xdeadbeefdeadbeef,
deadline: -1,
})
.try_to_vec()
.unwrap(),
vec![1, 239, 190, 173, 222, 239, 190, 173, 222, 255, 255, 255, 255, 255, 255, 255, 255],
);
}
#[test]
fn test_serialize_large_slice() {
let mut dst = vec![0xff; 4];
FeatureProposal::Expired.pack_into_slice(&mut dst);
assert_eq!(dst, vec![3, 0xff, 0xff, 0xff]);
}
#[test]
fn state_deserialize_invalid() {
assert_eq!(
FeatureProposal::unpack_from_slice(&[3]),
Ok(FeatureProposal::Expired),
);
assert_eq!(
FeatureProposal::unpack_from_slice(&[3, 0xff, 0xff, 0xff]),
Ok(FeatureProposal::Expired),
);
assert_eq!(
FeatureProposal::unpack_from_slice(&[4]),
Err(ProgramError::InvalidAccountData),
);
}
}