mod input;
use input::*;
mod output;
use output::*;
mod bytes;
mod parse;
use crate::{
program::finalize::{Finalize, FinalizeCommand},
Instruction,
};
use console::{
network::prelude::*,
program::{Identifier, Register, ValueType},
};
use indexmap::IndexSet;
#[derive(Clone, PartialEq, Eq)]
pub struct Function<N: Network> {
name: Identifier<N>,
inputs: IndexSet<Input<N>>,
instructions: Vec<Instruction<N>>,
outputs: IndexSet<Output<N>>,
finalize: Option<(FinalizeCommand<N>, Finalize<N>)>,
}
impl<N: Network> Function<N> {
pub fn new(name: Identifier<N>) -> Self {
Self { name, inputs: IndexSet::new(), instructions: Vec::new(), outputs: IndexSet::new(), finalize: None }
}
pub const fn name(&self) -> &Identifier<N> {
&self.name
}
pub const fn inputs(&self) -> &IndexSet<Input<N>> {
&self.inputs
}
pub fn input_types(&self) -> Vec<ValueType<N>> {
self.inputs.iter().map(|input| *input.value_type()).collect()
}
pub fn instructions(&self) -> &[Instruction<N>] {
&self.instructions
}
pub const fn outputs(&self) -> &IndexSet<Output<N>> {
&self.outputs
}
pub fn output_types(&self) -> Vec<ValueType<N>> {
self.outputs.iter().map(|output| *output.value_type()).collect()
}
pub const fn finalize(&self) -> &Option<(FinalizeCommand<N>, Finalize<N>)> {
&self.finalize
}
pub fn finalize_command(&self) -> Option<&FinalizeCommand<N>> {
self.finalize.as_ref().map(|(command, _)| command)
}
pub fn finalize_logic(&self) -> Option<&Finalize<N>> {
self.finalize.as_ref().map(|(_, finalize)| finalize)
}
}
impl<N: Network> Function<N> {
#[inline]
fn add_input(&mut self, input: Input<N>) -> Result<()> {
ensure!(self.instructions.is_empty(), "Cannot add inputs after instructions have been added");
ensure!(self.outputs.is_empty(), "Cannot add inputs after outputs have been added");
ensure!(self.inputs.len() <= N::MAX_INPUTS, "Cannot add more than {} inputs", N::MAX_INPUTS);
ensure!(!self.inputs.contains(&input), "Cannot add duplicate input statement");
ensure!(self.finalize.is_none(), "Cannot add instructions after finalize command has been added");
ensure!(matches!(input.register(), Register::Locator(..)), "Input register must be a locator");
self.inputs.insert(input);
Ok(())
}
#[inline]
pub fn add_instruction(&mut self, instruction: Instruction<N>) -> Result<()> {
ensure!(
self.instructions.len() <= N::MAX_INSTRUCTIONS,
"Cannot add more than {} instructions",
N::MAX_INSTRUCTIONS
);
ensure!(self.finalize.is_none(), "Cannot add instructions after finalize command has been added");
for register in instruction.destinations() {
ensure!(matches!(register, Register::Locator(..)), "Destination register must be a locator");
}
self.instructions.push(instruction);
Ok(())
}
#[inline]
fn add_output(&mut self, output: Output<N>) -> Result<()> {
ensure!(!self.instructions.is_empty(), "Cannot add outputs before instructions have been added");
ensure!(self.outputs.len() <= N::MAX_OUTPUTS, "Cannot add more than {} outputs", N::MAX_OUTPUTS);
ensure!(!self.outputs.contains(&output), "Cannot add duplicate output statement");
ensure!(self.finalize.is_none(), "Cannot add instructions after finalize command has been added");
self.outputs.insert(output);
Ok(())
}
#[inline]
fn add_finalize(&mut self, command: FinalizeCommand<N>, finalize: Finalize<N>) -> Result<()> {
ensure!(self.finalize.is_none(), "Cannot add multiple finalize scopes to function '{}'", self.name);
ensure!(*finalize.name() == self.name, "Finalize scope name must match function name '{}'", self.name);
ensure!(finalize.inputs().len() <= N::MAX_INPUTS, "Cannot add more than {} inputs to finalize", N::MAX_INPUTS);
ensure!(
command.operands().len() == finalize.inputs().len(),
"The 'finalize' command has {} operands, but 'finalize' takes {} inputs",
command.operands().len(),
finalize.inputs().len()
);
self.finalize = Some((command, finalize));
Ok(())
}
}
impl<N: Network> TypeName for Function<N> {
#[inline]
fn type_name() -> &'static str {
"function"
}
}