Rework 'blueprint apply' command and wrap up wiring up validation.
The apply command now works only from a serialized CBOR data (instead of a UPLC syntax). So it is no longer possible to specify arbitrary cbor terms through the CLI. I believe it to be an acceptable limitation for now; especially given that Aiken will never generate blueprints with non-data terms at the interface boundary.
This commit is contained in:
parent
bf222a3ea2
commit
4799af3242
|
@ -59,15 +59,15 @@ pub enum Error {
|
||||||
#[error("I couldn't find a definition corresponding to a reference.")]
|
#[error("I couldn't find a definition corresponding to a reference.")]
|
||||||
#[diagnostic(code("aiken::blueprint::apply::unknown::reference"))]
|
#[diagnostic(code("aiken::blueprint::apply::unknown::reference"))]
|
||||||
#[diagnostic(help(
|
#[diagnostic(help(
|
||||||
"While resolving a schema definition, I stumble upon an unknown reference:\n\n {reference}\n\nThis is unfortunate, but signals that either the reference is invalid or that the correspond schema definition is missing.",
|
"While resolving a schema definition, I stumbled upon an unknown reference:\n\n→ {reference}\n\nThis is unfortunate, but signals that either the reference is invalid or that the corresponding schema definition is missing. Double-check the blueprint for that reference or definition.",
|
||||||
reference = reference.as_json_pointer()
|
reference = reference.as_json_pointer().if_supports_color(Stdout, |s| s.red())
|
||||||
))]
|
))]
|
||||||
UnresolvedSchemaReference { reference: Reference },
|
UnresolvedSchemaReference { reference: Reference },
|
||||||
|
|
||||||
#[error("I caught a parameter application that seems off.")]
|
#[error("I caught a parameter application that seems off.")]
|
||||||
#[diagnostic(code("aiken::blueprint::apply::mismatch"))]
|
#[diagnostic(code("aiken::blueprint::apply::mismatch"))]
|
||||||
#[diagnostic(help(
|
#[diagnostic(help(
|
||||||
"When applying parameters to a validator, I control that the shape of the parameter you give me matches what is specified in the blueprint. Unfortunately, schemas didn't match in this case.\n\nI am expecting something of the shape:\n\n{expected}Which I couldn't match against the following term:\n\n{term}\n\nNote that this may only represent part of a bigger whole.",
|
"When applying parameters to a validator, I control that the shape of the parameter you give me matches what is specified in the blueprint. Unfortunately, it didn't match in this case.\n\nI am looking at the following value:\n\n{term}\n\nbut failed to match it against the specified schema:\n\n{expected}\n\n\nNOTE: this may only represent part of a bigger whole as I am validating the parameter incrementally.",
|
||||||
expected = serde_json::to_string_pretty(&schema).unwrap().if_supports_color(Stdout, |s| s.green()),
|
expected = serde_json::to_string_pretty(&schema).unwrap().if_supports_color(Stdout, |s| s.green()),
|
||||||
term = {
|
term = {
|
||||||
let mut buf = vec![];
|
let mut buf = vec![];
|
||||||
|
@ -92,6 +92,11 @@ pub enum Error {
|
||||||
found = found.if_supports_color(Stdout, |s| s.red()),
|
found = found.if_supports_color(Stdout, |s| s.red()),
|
||||||
))]
|
))]
|
||||||
TupleItemsMismatch { expected: usize, found: usize },
|
TupleItemsMismatch { expected: usize, found: usize },
|
||||||
|
|
||||||
|
#[error("I failed to convert some input into a valid parameter")]
|
||||||
|
#[diagnostic(code("aiken::blueprint::parse::parameter"))]
|
||||||
|
#[diagnostic(help("{hint}"))]
|
||||||
|
MalformedParameter { hint: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for Error {}
|
unsafe impl Send for Error {}
|
||||||
|
|
|
@ -154,11 +154,15 @@ impl Validator {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Validator {
|
impl Validator {
|
||||||
pub fn apply(self, arg: &Term<DeBruijn>) -> Result<Self, Error> {
|
pub fn apply(
|
||||||
|
self,
|
||||||
|
definitions: &Definitions<Annotated<Schema>>,
|
||||||
|
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((head, tail)) => {
|
Some((head, tail)) => {
|
||||||
head.validate(&self.definitions, arg)?;
|
head.validate(definitions, arg)?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
program: self.program.apply_term(arg),
|
program: self.program.apply_term(arg),
|
||||||
parameters: tail.to_vec(),
|
parameters: tail.to_vec(),
|
||||||
|
|
|
@ -391,7 +391,9 @@ where
|
||||||
|
|
||||||
let applied_validator =
|
let applied_validator =
|
||||||
blueprint.with_validator(title, when_too_many, when_missing, |validator| {
|
blueprint.with_validator(title, when_too_many, when_missing, |validator| {
|
||||||
validator.apply(param).map_err(|e| e.into())
|
validator
|
||||||
|
.apply(&blueprint.definitions, param)
|
||||||
|
.map_err(|e| e.into())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Overwrite validator
|
// Overwrite validator
|
||||||
|
|
|
@ -1,18 +1,22 @@
|
||||||
use crate::with_project;
|
use crate::with_project;
|
||||||
use aiken_project::error::Error;
|
use aiken_project::{blueprint, error::Error};
|
||||||
use miette::IntoDiagnostic;
|
use owo_colors::{OwoColorize, Stream::Stderr};
|
||||||
use std::{fs, path::PathBuf};
|
use std::{fs, path::PathBuf, process, rc::Rc};
|
||||||
use uplc::{
|
use uplc::ast::{Constant, DeBruijn, Term};
|
||||||
ast::{DeBruijn, Term},
|
|
||||||
parser,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Apply a parameter to a parameterized validator.
|
/// Apply a parameter to a parameterized validator.
|
||||||
#[derive(clap::Args)]
|
#[derive(clap::Args)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
|
/// The parameter, as a Plutus Data (CBOR, hex-encoded)
|
||||||
|
parameter: String,
|
||||||
|
|
||||||
/// Path to project
|
/// Path to project
|
||||||
directory: Option<PathBuf>,
|
directory: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// Output file. Optional, print on stdout when omitted.
|
||||||
|
#[clap(short, long)]
|
||||||
|
out: Option<PathBuf>,
|
||||||
|
|
||||||
/// Name of the validator's module within the project. Optional if there's only one validator.
|
/// Name of the validator's module within the project. Optional if there's only one validator.
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
module: Option<String>,
|
module: Option<String>,
|
||||||
|
@ -20,23 +24,58 @@ pub struct Args {
|
||||||
/// Name of the validator within the module. Optional if there's only one validator.
|
/// Name of the validator within the module. Optional if there's only one validator.
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
validator: Option<String>,
|
validator: Option<String>,
|
||||||
|
|
||||||
/// The parameter, using high-level UPLC-syntax
|
|
||||||
parameter: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exec(
|
pub fn exec(
|
||||||
Args {
|
Args {
|
||||||
|
parameter,
|
||||||
directory,
|
directory,
|
||||||
|
out,
|
||||||
module,
|
module,
|
||||||
validator,
|
validator,
|
||||||
parameter,
|
|
||||||
}: Args,
|
}: Args,
|
||||||
) -> miette::Result<()> {
|
) -> miette::Result<()> {
|
||||||
let term: Term<DeBruijn> = parser::term(¶meter)
|
eprintln!(
|
||||||
.into_diagnostic()?
|
"{} inputs",
|
||||||
.try_into()
|
" Parsing"
|
||||||
.into_diagnostic()?;
|
.if_supports_color(Stderr, |s| s.purple())
|
||||||
|
.if_supports_color(Stderr, |s| s.bold()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let bytes = hex::decode(parameter)
|
||||||
|
.map_err::<Error, _>(|e| {
|
||||||
|
blueprint::error::Error::MalformedParameter {
|
||||||
|
hint: format!("Invalid hex-encoded string: {e}"),
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
println!();
|
||||||
|
e.report();
|
||||||
|
process::exit(1)
|
||||||
|
});
|
||||||
|
|
||||||
|
let data = uplc::plutus_data(&bytes)
|
||||||
|
.map_err::<Error, _>(|e| {
|
||||||
|
blueprint::error::Error::MalformedParameter {
|
||||||
|
hint: format!("Invalid Plutus data; malformed CBOR encoding: {e}"),
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
println!();
|
||||||
|
e.report();
|
||||||
|
process::exit(1)
|
||||||
|
});
|
||||||
|
|
||||||
|
let term: Term<DeBruijn> = Term::Constant(Rc::new(Constant::Data(data)));
|
||||||
|
|
||||||
|
eprintln!(
|
||||||
|
"{} blueprint",
|
||||||
|
" Analyzing"
|
||||||
|
.if_supports_color(Stderr, |s| s.purple())
|
||||||
|
.if_supports_color(Stderr, |s| s.bold()),
|
||||||
|
);
|
||||||
|
|
||||||
with_project(directory, |p| {
|
with_project(directory, |p| {
|
||||||
let title = module.as_ref().map(|m| {
|
let title = module.as_ref().map(|m| {
|
||||||
|
@ -51,16 +90,35 @@ pub fn exec(
|
||||||
|
|
||||||
let title = title.as_ref().or(validator.as_ref());
|
let title = title.as_ref().or(validator.as_ref());
|
||||||
|
|
||||||
|
eprintln!(
|
||||||
|
"{} parameter",
|
||||||
|
" Applying"
|
||||||
|
.if_supports_color(Stderr, |s| s.purple())
|
||||||
|
.if_supports_color(Stderr, |s| s.bold()),
|
||||||
|
);
|
||||||
|
|
||||||
let blueprint = p.apply_parameter(title, &term)?;
|
let blueprint = p.apply_parameter(title, &term)?;
|
||||||
|
|
||||||
let json = serde_json::to_string_pretty(&blueprint).unwrap();
|
let json = serde_json::to_string_pretty(&blueprint).unwrap();
|
||||||
|
|
||||||
fs::write(p.blueprint_path(), json).map_err(|error| {
|
match out {
|
||||||
Error::FileIo {
|
None => {
|
||||||
|
println!("\n{}\n", json);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Some(ref path) => fs::write(path, json).map_err(|error| Error::FileIo {
|
||||||
error,
|
error,
|
||||||
path: p.blueprint_path(),
|
path: p.blueprint_path(),
|
||||||
}
|
}),
|
||||||
.into()
|
}?;
|
||||||
})
|
|
||||||
|
eprintln!(
|
||||||
|
"{}",
|
||||||
|
" Done"
|
||||||
|
.if_supports_color(Stderr, |s| s.purple())
|
||||||
|
.if_supports_color(Stderr, |s| s.bold()),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue