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),