mod decrement;
pub use decrement::*;
mod finalize;
pub use finalize::*;
mod increment;
pub use increment::*;
use crate::{program::Instruction, FinalizeRegisters, ProgramStorage, ProgramStore, Stack};
use console::network::prelude::*;
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Command<N: Network> {
Decrement(Decrement<N>),
Instruction(Instruction<N>),
Increment(Increment<N>),
}
impl<N: Network> Command<N> {
#[inline]
pub fn evaluate_finalize<P: ProgramStorage<N>>(
&self,
stack: &Stack<N>,
store: &ProgramStore<N, P>,
registers: &mut FinalizeRegisters<N>,
) -> Result<()> {
match self {
Command::Decrement(decrement) => decrement.evaluate_finalize(stack, store, registers),
Command::Instruction(_) => bail!("Instructions in 'finalize' are not supported (yet)."),
Command::Increment(increment) => increment.evaluate_finalize(stack, store, registers),
}
}
}
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::Decrement(Decrement::read_le(&mut reader)?)),
1 => Ok(Self::Instruction(Instruction::read_le(&mut reader)?)),
2 => Ok(Self::Increment(Increment::read_le(&mut reader)?)),
3.. => 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::Decrement(decrement) => {
0u8.write_le(&mut writer)?;
decrement.write_le(&mut writer)
}
Self::Instruction(instruction) => {
1u8.write_le(&mut writer)?;
instruction.write_le(&mut writer)
}
Self::Increment(increment) => {
2u8.write_le(&mut writer)?;
increment.write_le(&mut writer)
}
}
}
}
impl<N: Network> Parser for Command<N> {
#[inline]
fn parse(string: &str) -> ParserResult<Self> {
alt((
map(Decrement::parse, |decrement| Self::Decrement(decrement)),
map(Instruction::parse, |instruction| Self::Instruction(instruction)),
map(Increment::parse, |increment| Self::Increment(increment)),
))(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::Decrement(decrement) => Display::fmt(decrement, f),
Self::Instruction(instruction) => Display::fmt(instruction, f),
Self::Increment(increment) => Display::fmt(increment, 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;";
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 = "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;";
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;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
assert_eq!(Command::Decrement(Decrement::from_str(expected).unwrap()), command);
assert_eq!(expected, command.to_string());
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;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
assert_eq!(Command::Increment(Increment::from_str(expected).unwrap()), command);
assert_eq!(expected, command.to_string());
}
}