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.
This commit is contained in:
KtorZ 2023-02-04 11:39:55 +01:00
parent ea269b14a2
commit 9b8ff590b8
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
7 changed files with 112 additions and 14 deletions

View File

@ -129,14 +129,16 @@ impl<T> Validator<T>
where where
T: Clone, T: Clone,
{ {
pub fn apply(&mut self, arg: &Term<DeBruijn>) -> Result<(), Error> { pub fn apply(self, arg: &Term<DeBruijn>) -> Result<Self, Error> {
match self.parameters.split_first() { match self.parameters.split_first() {
None => Err(Error::NoParametersToApply), None => Err(Error::NoParametersToApply),
Some((_, tail)) => { Some((_, tail)) => {
// TODO: Ideally, we should control that the applied term matches its schema. // TODO: Ideally, we should control that the applied term matches its schema.
self.program = self.program.apply_term(arg); Ok(Self {
self.parameters = tail.to_vec(); program: self.program.apply_term(arg),
Ok(()) parameters: tail.to_vec(),
..self
})
} }
} }
} }

View File

@ -331,20 +331,32 @@ where
// Read blueprint // Read blueprint
let blueprint = File::open(self.blueprint_path()) let blueprint = File::open(self.blueprint_path())
.map_err(|_| blueprint::error::Error::InvalidOrMissingFile)?; .map_err(|_| blueprint::error::Error::InvalidOrMissingFile)?;
let blueprint: Blueprint<serde_json::Value> = let mut blueprint: Blueprint<serde_json::Value> =
serde_json::from_reader(BufReader::new(blueprint))?; serde_json::from_reader(BufReader::new(blueprint))?;
// Apply parameters // Apply parameters
let when_too_many = let when_too_many =
|known_validators| Error::MoreThanOneValidatorFound { known_validators }; |known_validators| Error::MoreThanOneValidatorFound { known_validators };
let when_missing = |known_validators| Error::NoValidatorNotFound { known_validators }; let when_missing = |known_validators| Error::NoValidatorNotFound { known_validators };
blueprint.with_validator( let applied_validator =
title, blueprint.with_validator(title, purpose, when_too_many, when_missing, |validator| {
purpose, validator.apply(param).map_err(|e| e.into())
when_too_many, })?;
when_missing,
|mut 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) Ok(blueprint)
} }

View File

@ -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<PathBuf>,
/// Name of the validator's module within the project. Optional if there's only one validator.
#[clap(short, long)]
validator: Option<String>,
/// Purpose of the validator within the module. Optional if there's only one validator.
#[clap(short, long, possible_values=&VALIDATOR_NAMES)]
purpose: Option<String>,
/// 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<DeBruijn> = parser::term(&parameter)
.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(),
})
})
}

View File

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

View File

@ -1,4 +1,4 @@
pub mod address; pub mod blueprint;
pub mod build; pub mod build;
pub mod check; pub mod check;
pub mod docs; pub mod docs;

View File

@ -1,5 +1,6 @@
use aiken::cmd::{ use aiken::cmd::{
address, build, check, docs, fmt, lsp, new, blueprint::{self, address},
build, check, docs, fmt, lsp, new,
packages::{self, add}, packages::{self, add},
tx, uplc, tx, uplc,
}; };
@ -19,6 +20,9 @@ pub enum Cmd {
Docs(docs::Args), Docs(docs::Args),
Add(add::Args), Add(add::Args),
#[clap(subcommand)]
Blueprint(blueprint::Cmd),
#[clap(subcommand)] #[clap(subcommand)]
Packages(packages::Cmd), Packages(packages::Cmd),
@ -48,6 +52,7 @@ fn main() -> miette::Result<()> {
Cmd::Check(args) => check::exec(args), Cmd::Check(args) => check::exec(args),
Cmd::Docs(args) => docs::exec(args), Cmd::Docs(args) => docs::exec(args),
Cmd::Add(args) => add::exec(args), Cmd::Add(args) => add::exec(args),
Cmd::Blueprint(args) => blueprint::exec(args),
Cmd::Packages(args) => packages::exec(args), Cmd::Packages(args) => packages::exec(args),
Cmd::Lsp(args) => lsp::exec(args), Cmd::Lsp(args) => lsp::exec(args),
Cmd::Tx(sub_cmd) => tx::exec(sub_cmd), Cmd::Tx(sub_cmd) => tx::exec(sub_cmd),