From 9b8ff590b8453f283d177b389a2f9a52fb84999f Mon Sep 17 00:00:00 2001 From: KtorZ Date: Sat, 4 Feb 2023 11:39:55 +0100 Subject: [PATCH] Implement 'blueprint apply' command. This is still a bit clunky as the interface is expecting parameters in UPLC form and we don't do any kind of verification. So it is easy to shoot oneself in the foot at the moment (for example, to apply an integer into something that should have received a data). To be improved later. --- .../aiken-project/src/blueprint/validator.rs | 10 ++-- crates/aiken-project/src/lib.rs | 28 ++++++--- .../aiken/src/cmd/{ => blueprint}/address.rs | 0 crates/aiken/src/cmd/blueprint/apply.rs | 60 +++++++++++++++++++ crates/aiken/src/cmd/blueprint/mod.rs | 19 ++++++ crates/aiken/src/cmd/mod.rs | 2 +- crates/aiken/src/main.rs | 7 ++- 7 files changed, 112 insertions(+), 14 deletions(-) rename crates/aiken/src/cmd/{ => blueprint}/address.rs (100%) create mode 100644 crates/aiken/src/cmd/blueprint/apply.rs create mode 100644 crates/aiken/src/cmd/blueprint/mod.rs diff --git a/crates/aiken-project/src/blueprint/validator.rs b/crates/aiken-project/src/blueprint/validator.rs index 84e86645..18f230f9 100644 --- a/crates/aiken-project/src/blueprint/validator.rs +++ b/crates/aiken-project/src/blueprint/validator.rs @@ -129,14 +129,16 @@ impl Validator where T: Clone, { - pub fn apply(&mut self, arg: &Term) -> Result<(), Error> { + pub fn apply(self, arg: &Term) -> Result { match self.parameters.split_first() { None => Err(Error::NoParametersToApply), Some((_, tail)) => { // TODO: Ideally, we should control that the applied term matches its schema. - self.program = self.program.apply_term(arg); - self.parameters = tail.to_vec(); - Ok(()) + Ok(Self { + program: self.program.apply_term(arg), + parameters: tail.to_vec(), + ..self + }) } } } diff --git a/crates/aiken-project/src/lib.rs b/crates/aiken-project/src/lib.rs index 5f79b988..d54c2ff4 100644 --- a/crates/aiken-project/src/lib.rs +++ b/crates/aiken-project/src/lib.rs @@ -331,20 +331,32 @@ where // Read blueprint let blueprint = File::open(self.blueprint_path()) .map_err(|_| blueprint::error::Error::InvalidOrMissingFile)?; - let blueprint: Blueprint = + let mut blueprint: Blueprint = serde_json::from_reader(BufReader::new(blueprint))?; // Apply parameters let when_too_many = |known_validators| Error::MoreThanOneValidatorFound { known_validators }; let when_missing = |known_validators| Error::NoValidatorNotFound { known_validators }; - blueprint.with_validator( - title, - purpose, - when_too_many, - when_missing, - |mut validator| validator.apply(param).map_err(|e| e.into()), - )?; + let applied_validator = + blueprint.with_validator(title, purpose, when_too_many, when_missing, |validator| { + validator.apply(param).map_err(|e| e.into()) + })?; + + // Overwrite validator + blueprint.validators = blueprint + .validators + .into_iter() + .map(|validator| { + let same_title = validator.title == applied_validator.title; + let same_purpose = validator.purpose == applied_validator.purpose; + if same_title && same_purpose { + applied_validator.to_owned() + } else { + validator + } + }) + .collect(); Ok(blueprint) } diff --git a/crates/aiken/src/cmd/address.rs b/crates/aiken/src/cmd/blueprint/address.rs similarity index 100% rename from crates/aiken/src/cmd/address.rs rename to crates/aiken/src/cmd/blueprint/address.rs diff --git a/crates/aiken/src/cmd/blueprint/apply.rs b/crates/aiken/src/cmd/blueprint/apply.rs new file mode 100644 index 00000000..6f290146 --- /dev/null +++ b/crates/aiken/src/cmd/blueprint/apply.rs @@ -0,0 +1,60 @@ +use crate::with_project; +use aiken_lang::VALIDATOR_NAMES; +use aiken_project::error::Error; +use miette::IntoDiagnostic; +use std::{fs, path::PathBuf}; +use uplc::{ + ast::{DeBruijn, Term}, + parser, +}; + +#[derive(clap::Args)] +#[clap(setting(clap::AppSettings::DeriveDisplayOrder))] +/// Apply a parameter to a parameterized validator. +pub struct Args { + /// Path to project + directory: Option, + + /// Name of the validator's module within the project. Optional if there's only one validator. + #[clap(short, long)] + validator: Option, + + /// Purpose of the validator within the module. Optional if there's only one validator. + #[clap(short, long, possible_values=&VALIDATOR_NAMES)] + purpose: Option, + + /// The parameter, using high-level UPLC-syntax + #[clap(long)] + parameter: String, +} + +pub fn exec( + Args { + directory, + validator, + purpose, + parameter, + }: Args, +) -> miette::Result<()> { + let term: Term = parser::term(¶meter) + .into_diagnostic()? + .try_into() + .into_diagnostic()?; + + with_project(directory, |p| { + let blueprint = p.apply_parameter( + validator.as_ref(), + purpose + .as_ref() + .map(|p| p.clone().try_into().unwrap()) + .as_ref(), + &term, + )?; + + let json = serde_json::to_string_pretty(&blueprint).unwrap(); + fs::write(p.blueprint_path(), json).map_err(|error| Error::FileIo { + error, + path: p.blueprint_path(), + }) + }) +} diff --git a/crates/aiken/src/cmd/blueprint/mod.rs b/crates/aiken/src/cmd/blueprint/mod.rs new file mode 100644 index 00000000..0dde4d8a --- /dev/null +++ b/crates/aiken/src/cmd/blueprint/mod.rs @@ -0,0 +1,19 @@ +pub mod address; +pub mod apply; + +use clap::Subcommand; + +/// Commands for working with Plutus blueprints +#[derive(Subcommand)] +#[clap(setting(clap::AppSettings::DeriveDisplayOrder))] +pub enum Cmd { + Address(address::Args), + Apply(apply::Args), +} + +pub fn exec(cmd: Cmd) -> miette::Result<()> { + match cmd { + Cmd::Address(args) => address::exec(args), + Cmd::Apply(args) => apply::exec(args), + } +} diff --git a/crates/aiken/src/cmd/mod.rs b/crates/aiken/src/cmd/mod.rs index 1f69dae9..bfef865e 100644 --- a/crates/aiken/src/cmd/mod.rs +++ b/crates/aiken/src/cmd/mod.rs @@ -1,4 +1,4 @@ -pub mod address; +pub mod blueprint; pub mod build; pub mod check; pub mod docs; diff --git a/crates/aiken/src/main.rs b/crates/aiken/src/main.rs index 597f134a..f3bc0fc4 100644 --- a/crates/aiken/src/main.rs +++ b/crates/aiken/src/main.rs @@ -1,5 +1,6 @@ use aiken::cmd::{ - address, build, check, docs, fmt, lsp, new, + blueprint::{self, address}, + build, check, docs, fmt, lsp, new, packages::{self, add}, tx, uplc, }; @@ -19,6 +20,9 @@ pub enum Cmd { Docs(docs::Args), Add(add::Args), + #[clap(subcommand)] + Blueprint(blueprint::Cmd), + #[clap(subcommand)] Packages(packages::Cmd), @@ -48,6 +52,7 @@ fn main() -> miette::Result<()> { Cmd::Check(args) => check::exec(args), Cmd::Docs(args) => docs::exec(args), Cmd::Add(args) => add::exec(args), + Cmd::Blueprint(args) => blueprint::exec(args), Cmd::Packages(args) => packages::exec(args), Cmd::Lsp(args) => lsp::exec(args), Cmd::Tx(sub_cmd) => tx::exec(sub_cmd),