use solana_program::{program_error::ProgramError, pubkey::Pubkey};
pub fn parse_instruction<'a, T: std::convert::TryFrom<u8>>(
api_id: &'a Pubkey,
program_id: &'a Pubkey,
data: &'a [u8],
) -> Result<(T, &'a [u8]), ProgramError> {
if program_id.ne(&api_id) {
return Err(ProgramError::IncorrectProgramId);
}
let (tag, data) = data
.split_first()
.ok_or(ProgramError::InvalidInstructionData)?;
let ix = T::try_from(*tag).or(Err(ProgramError::InvalidInstructionData))?;
Ok((ix, data))
}
pub fn string_to_bytes<const N: usize>(s: &str) -> Result<[u8; N], ProgramError> {
let mut bytes = [0; N];
let s_bytes = s.as_bytes();
if s_bytes.len() > N {
return Err(ProgramError::Custom(ERROR_STRING_TOO_LONG));
}
let len = s_bytes.len();
bytes[..len].copy_from_slice(&s_bytes[..len]);
Ok(bytes)
}
pub fn bytes_to_string<const N: usize>(bytes: &[u8; N]) -> Result<String, ProgramError> {
let actual_len = bytes.iter().position(|&b| b == 0).unwrap_or(N);
Ok(String::from_utf8_lossy(&bytes[..actual_len])
.trim_matches('\0')
.to_string())
}
pub const ERROR_STRING_TOO_LONG: u32 = 1;
pub const ERROR_INVALID_UTF8: u32 = 2;
#[test]
fn test_string_to_bytes() {
let result = string_to_bytes::<5>("hello");
assert!(result.is_ok());
assert_eq!(result.unwrap(), *b"hello");
let result = string_to_bytes::<5>("hi");
assert!(result.is_ok());
assert_eq!(&result.unwrap()[..2], b"hi");
let result = string_to_bytes::<3>("hello");
assert!(result.is_err());
assert_eq!(
result.unwrap_err(),
ProgramError::Custom(ERROR_STRING_TOO_LONG)
);
}
#[test]
fn test_bytes_to_string() {
let bytes = *b"hello";
let result = bytes_to_string::<5>(&bytes);
assert!(result.is_ok());
assert_eq!(result.unwrap(), "hello");
let mut bytes = [0u8; 5];
bytes[..3].copy_from_slice(b"hi\0");
let result = bytes_to_string::<5>(&bytes);
assert!(result.is_ok());
assert_eq!(result.unwrap(), "hi");
}