use super::*;
impl<N: Network> Future<N> {
pub fn find<A: Into<Access<N>> + Copy + Debug>(&self, path: &[A]) -> Result<Value<N>> {
ensure!(!path.is_empty(), "Attempted to find an argument with an empty path.");
enum ArgumentRefType<'a, N: Network> {
Plaintext(&'a Plaintext<N>),
Future(&'a Future<N>),
DynamicFuture(&'a DynamicFuture<N>),
}
let mut value = ArgumentRefType::Future(self);
for access in path.iter() {
let access = (*access).into();
match (value, access) {
(ArgumentRefType::Plaintext(Plaintext::Struct(members, ..)), Access::Member(identifier)) => {
match members.get(&identifier) {
Some(member) => value = ArgumentRefType::Plaintext(member),
None => bail!("Failed to locate member '{identifier}'"),
}
}
(ArgumentRefType::Plaintext(Plaintext::Array(array, ..)), Access::Index(index)) => {
match array.get(*index as usize) {
Some(element) => value = ArgumentRefType::Plaintext(element),
None => bail!("Index '{index}' is out of bounds"),
}
}
(ArgumentRefType::Future(future), Access::Index(index)) => {
match future.arguments.get(*index as usize) {
Some(Argument::Future(future)) => value = ArgumentRefType::Future(future),
Some(Argument::Plaintext(plaintext)) => value = ArgumentRefType::Plaintext(plaintext),
Some(Argument::DynamicFuture(dynamic_future)) => {
value = ArgumentRefType::DynamicFuture(dynamic_future)
}
None => bail!("Index '{index}' is out of bounds"),
}
}
_ => bail!("Invalid access `{access}`"),
}
}
match value {
ArgumentRefType::Plaintext(plaintext) => Ok(Value::Plaintext(plaintext.clone())),
ArgumentRefType::Future(future) => Ok(Value::Future(future.clone())),
ArgumentRefType::DynamicFuture(dynamic_future) => Ok(Value::DynamicFuture(dynamic_future.clone())),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use snarkvm_console_network::MainnetV0;
use core::str::FromStr;
type CurrentNetwork = MainnetV0;
#[test]
fn test_find_plaintext_argument() {
let future = Future::<CurrentNetwork>::new(
ProgramID::from_str("test.aleo").unwrap(),
Identifier::from_str("foo").unwrap(),
vec![
Argument::Plaintext(Plaintext::from_str("100u64").unwrap()),
Argument::Plaintext(Plaintext::from_str("200u64").unwrap()),
],
);
let value = future.find(&[Access::Index(U32::new(0))]).unwrap();
assert_eq!(value, Value::Plaintext(Plaintext::from_str("100u64").unwrap()));
let value = future.find(&[Access::Index(U32::new(1))]).unwrap();
assert_eq!(value, Value::Plaintext(Plaintext::from_str("200u64").unwrap()));
}
#[test]
fn test_find_nested_future_argument() {
let inner = Future::<CurrentNetwork>::new(
ProgramID::from_str("inner.aleo").unwrap(),
Identifier::from_str("bar").unwrap(),
vec![Argument::Plaintext(Plaintext::from_str("42u64").unwrap())],
);
let outer = Future::<CurrentNetwork>::new(
ProgramID::from_str("outer.aleo").unwrap(),
Identifier::from_str("baz").unwrap(),
vec![Argument::Future(inner.clone())],
);
let value = outer.find(&[Access::Index(U32::new(0))]).unwrap();
assert_eq!(value, Value::Future(inner));
}
#[test]
fn test_find_dynamic_future_argument() {
let inner = Future::<CurrentNetwork>::new(
ProgramID::from_str("inner.aleo").unwrap(),
Identifier::from_str("bar").unwrap(),
vec![Argument::Plaintext(Plaintext::from_str("42u64").unwrap())],
);
let dynamic_inner = DynamicFuture::from_future(&inner).unwrap();
let outer = Future::<CurrentNetwork>::new(
ProgramID::from_str("outer.aleo").unwrap(),
Identifier::from_str("baz").unwrap(),
vec![Argument::DynamicFuture(dynamic_inner.clone())],
);
let value = outer.find(&[Access::Index(U32::new(0))]).unwrap();
match value {
Value::DynamicFuture(result) => {
assert_eq!(result.program_name(), dynamic_inner.program_name());
assert_eq!(result.program_network(), dynamic_inner.program_network());
assert_eq!(result.function_name(), dynamic_inner.function_name());
assert_eq!(result.checksum(), dynamic_inner.checksum());
}
_ => panic!("Expected DynamicFuture value"),
}
}
#[test]
fn test_find_mixed_arguments() {
let inner = Future::<CurrentNetwork>::new(
ProgramID::from_str("inner.aleo").unwrap(),
Identifier::from_str("bar").unwrap(),
vec![],
);
let dynamic_inner = DynamicFuture::from_future(&inner).unwrap();
let future = Future::<CurrentNetwork>::new(
ProgramID::from_str("test.aleo").unwrap(),
Identifier::from_str("mixed").unwrap(),
vec![
Argument::Plaintext(Plaintext::from_str("100u64").unwrap()),
Argument::Future(inner.clone()),
Argument::DynamicFuture(dynamic_inner.clone()),
],
);
let value = future.find(&[Access::Index(U32::new(0))]).unwrap();
assert!(matches!(value, Value::Plaintext(_)));
let value = future.find(&[Access::Index(U32::new(1))]).unwrap();
assert!(matches!(value, Value::Future(_)));
let value = future.find(&[Access::Index(U32::new(2))]).unwrap();
assert!(matches!(value, Value::DynamicFuture(_)));
}
#[test]
fn test_find_out_of_bounds() {
let future = Future::<CurrentNetwork>::new(
ProgramID::from_str("test.aleo").unwrap(),
Identifier::from_str("foo").unwrap(),
vec![Argument::Plaintext(Plaintext::from_str("100u64").unwrap())],
);
let result = future.find(&[Access::Index(U32::new(5))]);
assert!(result.is_err());
}
#[test]
fn test_dynamic_future_fields_cannot_be_accessed() {
let inner = Future::<CurrentNetwork>::new(
ProgramID::from_str("inner.aleo").unwrap(),
Identifier::from_str("bar").unwrap(),
vec![Argument::Plaintext(Plaintext::from_str("42u64").unwrap())],
);
let dynamic_inner = DynamicFuture::from_future(&inner).unwrap();
let outer = Future::<CurrentNetwork>::new(
ProgramID::from_str("outer.aleo").unwrap(),
Identifier::from_str("baz").unwrap(),
vec![Argument::DynamicFuture(dynamic_inner)],
);
let value = outer.find(&[Access::Index(U32::new(0))]).unwrap();
assert!(matches!(value, Value::DynamicFuture(_)));
let result = outer.find(&[Access::Index(U32::new(0)), Access::Index(U32::new(0))]);
assert!(result.is_err());
}
}