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:
parent
ea269b14a2
commit
9b8ff590b8
|
@ -129,14 +129,16 @@ impl<T> Validator<T>
|
|||
where
|
||||
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() {
|
||||
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
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -331,20 +331,32 @@ where
|
|||
// Read blueprint
|
||||
let blueprint = File::open(self.blueprint_path())
|
||||
.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))?;
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
|
|
@ -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(¶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(),
|
||||
})
|
||||
})
|
||||
}
|
|
@ -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),
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
pub mod address;
|
||||
pub mod blueprint;
|
||||
pub mod build;
|
||||
pub mod check;
|
||||
pub mod docs;
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Reference in New Issue