mod finalize;
pub use finalize::*;
mod get;
pub use get::*;
mod get_or_init;
pub use get_or_init::*;
mod set;
pub use set::*;
use crate::{program::Instruction, FinalizeOperation, FinalizeRegisters, FinalizeStorage, FinalizeStore, Stack};
use console::network::prelude::*;
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Command<N: Network> {
Instruction(Instruction<N>),
Get(Get<N>),
GetOrInit(GetOrInit<N>),
Set(Set<N>),
}
impl<N: Network> Command<N> {
#[inline]
pub fn finalize<P: FinalizeStorage<N>>(
&self,
stack: &Stack<N>,
store: &FinalizeStore<N, P>,
registers: &mut FinalizeRegisters<N>,
) -> Result<Option<FinalizeOperation<N>>> {
match self {
Command::Instruction(instruction) => instruction.finalize(stack, registers).map(|_| None),
Command::Get(get) => get.finalize(stack, store, registers).map(|_| None),
Command::GetOrInit(get_or_init) => get_or_init.finalize(stack, store, registers),
Command::Set(set) => set.finalize(stack, store, registers).map(Some),
}
}
}
impl<N: Network> FromBytes for Command<N> {
fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
let variant = u8::read_le(&mut reader)?;
match variant {
0 => Ok(Self::Instruction(Instruction::read_le(&mut reader)?)),
1 => Ok(Self::Get(Get::read_le(&mut reader)?)),
2 => Ok(Self::GetOrInit(GetOrInit::read_le(&mut reader)?)),
3 => Ok(Self::Set(Set::read_le(&mut reader)?)),
4.. => Err(error(format!("Invalid command variant: {variant}"))),
}
}
}
impl<N: Network> ToBytes for Command<N> {
fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
match self {
Self::Instruction(instruction) => {
0u8.write_le(&mut writer)?;
instruction.write_le(&mut writer)
}
Self::Get(get) => {
1u8.write_le(&mut writer)?;
get.write_le(&mut writer)
}
Self::GetOrInit(get_or_init) => {
2u8.write_le(&mut writer)?;
get_or_init.write_le(&mut writer)
}
Self::Set(set) => {
3u8.write_le(&mut writer)?;
set.write_le(&mut writer)
}
}
}
}
impl<N: Network> Parser for Command<N> {
#[inline]
fn parse(string: &str) -> ParserResult<Self> {
alt((
map(GetOrInit::parse, |get_or_init| Self::GetOrInit(get_or_init)),
map(Get::parse, |get| Self::Get(get)),
map(Set::parse, |set| Self::Set(set)),
map(Instruction::parse, |instruction| Self::Instruction(instruction)),
))(string)
}
}
impl<N: Network> FromStr for Command<N> {
type Err = Error;
#[inline]
fn from_str(string: &str) -> Result<Self> {
match Self::parse(string) {
Ok((remainder, object)) => {
ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
Ok(object)
}
Err(error) => bail!("Failed to parse string. {error}"),
}
}
}
impl<N: Network> Debug for Command<N> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
impl<N: Network> Display for Command<N> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Instruction(instruction) => Display::fmt(instruction, f),
Self::Get(get) => Display::fmt(get, f),
Self::GetOrInit(get_or_init) => Display::fmt(get_or_init, f),
Self::Set(set) => Display::fmt(set, f),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use console::network::Testnet3;
type CurrentNetwork = Testnet3;
#[test]
fn test_command_bytes() {
let expected = "decrement object[r0] by r1;";
Command::<CurrentNetwork>::parse(expected).unwrap_err();
let expected = "add r0 r1 into r2;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
let bytes = command.to_bytes_le().unwrap();
assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
let expected = "increment object[r0] by r1;";
Command::<CurrentNetwork>::parse(expected).unwrap_err();
let expected = "get object[r0] into r1;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
let bytes = command.to_bytes_le().unwrap();
assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
let expected = "get.or_init object[r0] r1 into r2;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
let bytes = command.to_bytes_le().unwrap();
assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
let expected = "set r0 into object[r1];";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
let bytes = command.to_bytes_le().unwrap();
assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
}
#[test]
fn test_command_parse() {
let expected = "decrement object[r0] by r1;";
Command::<CurrentNetwork>::parse(expected).unwrap_err();
let expected = "add r0 r1 into r2;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
assert_eq!(Command::Instruction(Instruction::from_str(expected).unwrap()), command);
assert_eq!(expected, command.to_string());
let expected = "increment object[r0] by r1;";
Command::<CurrentNetwork>::parse(expected).unwrap_err();
let expected = "get object[r0] into r1;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
assert_eq!(Command::Get(Get::from_str(expected).unwrap()), command);
assert_eq!(expected, command.to_string());
let expected = "get.or_init object[r0] r1 into r2;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
assert_eq!(Command::GetOrInit(GetOrInit::from_str(expected).unwrap()), command);
assert_eq!(expected, command.to_string());
let expected = "set r0 into object[r1];";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
assert_eq!(Command::Set(Set::from_str(expected).unwrap()), command);
assert_eq!(expected, command.to_string());
}
}