Merge pull request #335 from aiken-lang/blueprint-parameters
Blueprint parameters
This commit is contained in:
commit
8feaefe073
|
@ -27,6 +27,7 @@ pub enum Error {
|
||||||
source_code: NamedSource,
|
source_code: NamedSource,
|
||||||
return_type: Arc<Type>,
|
return_type: Arc<Type>,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("A {} validator requires at least {} arguments.", name.purple().bold(), at_least.to_string().purple().bold())]
|
#[error("A {} validator requires at least {} arguments.", name.purple().bold(), at_least.to_string().purple().bold())]
|
||||||
#[diagnostic(code("aiken::blueprint::invalid::arity"))]
|
#[diagnostic(code("aiken::blueprint::invalid::arity"))]
|
||||||
WrongValidatorArity {
|
WrongValidatorArity {
|
||||||
|
@ -37,12 +38,13 @@ pub enum Error {
|
||||||
#[source_code]
|
#[source_code]
|
||||||
source_code: NamedSource,
|
source_code: NamedSource,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("{}", error)]
|
#[error("{}", error)]
|
||||||
#[diagnostic(help("{}", error.help()))]
|
#[diagnostic(help("{}", error.help()))]
|
||||||
#[diagnostic(code("aiken::blueprint::interface"))]
|
#[diagnostic(code("aiken::blueprint::interface"))]
|
||||||
Schema {
|
Schema {
|
||||||
error: schema::Error,
|
error: schema::Error,
|
||||||
#[label("invalid contract's boundary")]
|
#[label("invalid validator's boundary")]
|
||||||
location: Span,
|
location: Span,
|
||||||
#[source_code]
|
#[source_code]
|
||||||
source_code: NamedSource,
|
source_code: NamedSource,
|
||||||
|
@ -52,6 +54,15 @@ pub enum Error {
|
||||||
#[diagnostic(code("aiken::blueprint::missing"))]
|
#[diagnostic(code("aiken::blueprint::missing"))]
|
||||||
#[diagnostic(help("Did you forget to {build} the project?", build = "build".purple().bold()))]
|
#[diagnostic(help("Did you forget to {build} the project?", build = "build".purple().bold()))]
|
||||||
InvalidOrMissingFile,
|
InvalidOrMissingFile,
|
||||||
|
|
||||||
|
#[error("I didn't find any parameters to apply in the given validator.")]
|
||||||
|
#[diagnostic(code("aiken::blueprint::apply::no_parameters"))]
|
||||||
|
NoParametersToApply,
|
||||||
|
|
||||||
|
#[error("I couldn't compute the address of the given validator because it's parameterized by {} parameter(s)!", format!("{n}").purple())]
|
||||||
|
#[diagnostic(code("aiken::blueprint::address::parameterized"))]
|
||||||
|
#[diagnostic(help("I can only compute addresses of validators that are fully applied. For example, a {keyword_spend} validator must have exactly 3 arguments: a datum, a redeemer and a context. If it has more, they need to be provided beforehand and applied directly in the validator. Applying parameters change the validator's compiled code, and thus the address.\n\nThis is why I need you to apply parmeters first.", keyword_spend = "spend".purple()))]
|
||||||
|
ParameterizedValidator { n: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assert_return_bool(module: &CheckedModule, def: &TypedFunction) -> Result<(), Error> {
|
pub fn assert_return_bool(module: &CheckedModule, def: &TypedFunction) -> Result<(), Error> {
|
||||||
|
|
|
@ -7,12 +7,12 @@ use aiken_lang::uplc::CodeGenerator;
|
||||||
use error::Error;
|
use error::Error;
|
||||||
use schema::Schema;
|
use schema::Schema;
|
||||||
use std::fmt::{self, Debug, Display};
|
use std::fmt::{self, Debug, Display};
|
||||||
use validator::Validator;
|
use validator::{Purpose, Validator};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct Blueprint<T> {
|
pub struct Blueprint<T: Default> {
|
||||||
pub preamble: Preamble,
|
pub preamble: Preamble,
|
||||||
pub validators: Vec<validator::Validator<T>>,
|
pub validators: Vec<Validator<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
@ -25,6 +25,12 @@ pub struct Preamble {
|
||||||
pub license: Option<String>,
|
pub license: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum LookupResult<'a, T> {
|
||||||
|
One(&'a T),
|
||||||
|
Many,
|
||||||
|
}
|
||||||
|
|
||||||
impl Blueprint<Schema> {
|
impl Blueprint<Schema> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
|
@ -47,6 +53,59 @@ impl Blueprint<Schema> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Blueprint<T>
|
||||||
|
where
|
||||||
|
T: Clone + Default,
|
||||||
|
{
|
||||||
|
pub fn lookup(
|
||||||
|
&self,
|
||||||
|
title: Option<&String>,
|
||||||
|
purpose: Option<&Purpose>,
|
||||||
|
) -> Option<LookupResult<Validator<T>>> {
|
||||||
|
let mut validator = None;
|
||||||
|
for v in self.validators.iter() {
|
||||||
|
let match_title = Some(&v.title) == title.or(Some(&v.title));
|
||||||
|
let match_purpose = Some(&v.purpose) == purpose.or(Some(&v.purpose));
|
||||||
|
if match_title && match_purpose {
|
||||||
|
validator = Some(if validator.is_none() {
|
||||||
|
LookupResult::One(v)
|
||||||
|
} else {
|
||||||
|
LookupResult::Many
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
validator
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_validator<F, A, E>(
|
||||||
|
&self,
|
||||||
|
title: Option<&String>,
|
||||||
|
purpose: Option<&Purpose>,
|
||||||
|
when_missing: fn(Vec<(String, Purpose)>) -> E,
|
||||||
|
when_too_many: fn(Vec<(String, Purpose)>) -> E,
|
||||||
|
action: F,
|
||||||
|
) -> Result<A, E>
|
||||||
|
where
|
||||||
|
F: Fn(Validator<T>) -> Result<A, E>,
|
||||||
|
{
|
||||||
|
match self.lookup(title, purpose) {
|
||||||
|
Some(LookupResult::One(validator)) => action(validator.to_owned()),
|
||||||
|
Some(LookupResult::Many) => Err(when_too_many(
|
||||||
|
self.validators
|
||||||
|
.iter()
|
||||||
|
.map(|v| (v.title.clone(), v.purpose.clone()))
|
||||||
|
.collect(),
|
||||||
|
)),
|
||||||
|
None => Err(when_missing(
|
||||||
|
self.validators
|
||||||
|
.iter()
|
||||||
|
.map(|v| (v.title.clone(), v.purpose.clone()))
|
||||||
|
.collect(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for Blueprint<Schema> {
|
impl Display for Blueprint<Schema> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let s = serde_json::to_string_pretty(self).map_err(|_| fmt::Error)?;
|
let s = serde_json::to_string_pretty(self).map_err(|_| fmt::Error)?;
|
||||||
|
|
|
@ -302,6 +302,12 @@ impl Data {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Schema {
|
||||||
|
fn default() -> Self {
|
||||||
|
Schema::Unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for Schema {
|
impl Display for Schema {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let s = serde_json::to_string_pretty(self).map_err(|_| fmt::Error)?;
|
let s = serde_json::to_string_pretty(self).map_err(|_| fmt::Error)?;
|
||||||
|
|
|
@ -10,7 +10,7 @@ use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt::{self, Display},
|
fmt::{self, Display},
|
||||||
};
|
};
|
||||||
use uplc::ast::{DeBruijn, Program};
|
use uplc::ast::{DeBruijn, Program, Term};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct Validator<T> {
|
pub struct Validator<T> {
|
||||||
|
@ -21,6 +21,9 @@ pub struct Validator<T> {
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub datum: Option<Annotated<T>>,
|
pub datum: Option<Annotated<T>>,
|
||||||
pub redeemer: Annotated<T>,
|
pub redeemer: Annotated<T>,
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
#[serde(default)]
|
||||||
|
pub parameters: Vec<Annotated<T>>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub program: Program<DeBruijn>,
|
pub program: Program<DeBruijn>,
|
||||||
}
|
}
|
||||||
|
@ -58,12 +61,39 @@ impl Validator<Schema> {
|
||||||
assert_min_arity(validator, def, purpose.min_arity())?;
|
assert_min_arity(validator, def, purpose.min_arity())?;
|
||||||
|
|
||||||
let mut args = def.arguments.iter().rev();
|
let mut args = def.arguments.iter().rev();
|
||||||
let (_, redeemer, datum) = (args.next(), args.next().unwrap(), args.next());
|
let (_, redeemer) = (args.next(), args.next().unwrap());
|
||||||
|
let datum = if purpose.min_arity() > 2 {
|
||||||
|
args.next()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Validator {
|
Ok(Validator {
|
||||||
title: validator.name.clone(),
|
title: validator.name.clone(),
|
||||||
description: None,
|
description: None,
|
||||||
purpose,
|
purpose,
|
||||||
|
parameters: args
|
||||||
|
.rev()
|
||||||
|
.map(|param| {
|
||||||
|
let annotation =
|
||||||
|
Annotated::from_type(modules.into(), ¶m.tipo, &HashMap::new()).map_err(
|
||||||
|
|error| Error::Schema {
|
||||||
|
error,
|
||||||
|
location: param.location,
|
||||||
|
source_code: NamedSource::new(
|
||||||
|
validator.input_path.display().to_string(),
|
||||||
|
validator.code.clone(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
annotation.map(|mut annotation| {
|
||||||
|
annotation.title = annotation
|
||||||
|
.title
|
||||||
|
.or_else(|| Some(param.arg_name.get_label()));
|
||||||
|
annotation
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Result<_, _>>()?,
|
||||||
datum: datum
|
datum: datum
|
||||||
.map(|datum| {
|
.map(|datum| {
|
||||||
Annotated::from_type(modules.into(), &datum.tipo, &HashMap::new()).map_err(
|
Annotated::from_type(modules.into(), &datum.tipo, &HashMap::new()).map_err(
|
||||||
|
@ -95,6 +125,25 @@ impl Validator<Schema> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Validator<T>
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
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.
|
||||||
|
Ok(Self {
|
||||||
|
program: self.program.apply_term(arg),
|
||||||
|
parameters: tail.to_vec(),
|
||||||
|
..self
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Purpose {
|
impl Purpose {
|
||||||
pub fn min_arity(&self) -> u8 {
|
pub fn min_arity(&self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
|
@ -272,6 +321,32 @@ mod test {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validator_mint_parameterized() {
|
||||||
|
assert_validator(
|
||||||
|
r#"
|
||||||
|
fn mint(utxo_ref: Int, redeemer: Data, ctx: Data) {
|
||||||
|
True
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
json!({
|
||||||
|
"title": "test_module",
|
||||||
|
"purpose": "mint",
|
||||||
|
"hash": "455f24922a520c59499fdafad95e1272fab81a99452f6b9545f95337",
|
||||||
|
"parameters": [{
|
||||||
|
"title": "utxo_ref",
|
||||||
|
"dataType": "integer"
|
||||||
|
|
||||||
|
}],
|
||||||
|
"redeemer": {
|
||||||
|
"title": "Data",
|
||||||
|
"description": "Any Plutus data."
|
||||||
|
},
|
||||||
|
"compiledCode": "4d01000022253335734944526161"
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn validator_spend() {
|
fn validator_spend() {
|
||||||
assert_validator(
|
assert_validator(
|
||||||
|
|
|
@ -37,7 +37,7 @@ use std::{
|
||||||
};
|
};
|
||||||
use telemetry::EventListener;
|
use telemetry::EventListener;
|
||||||
use uplc::{
|
use uplc::{
|
||||||
ast::{Constant, Term},
|
ast::{Constant, DeBruijn, Term},
|
||||||
machine::cost_model::ExBudget,
|
machine::cost_model::ExBudget,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -283,8 +283,8 @@ where
|
||||||
|
|
||||||
pub fn address(
|
pub fn address(
|
||||||
&self,
|
&self,
|
||||||
with_title: Option<&String>,
|
title: Option<&String>,
|
||||||
with_purpose: Option<&validator::Purpose>,
|
purpose: Option<&validator::Purpose>,
|
||||||
stake_address: Option<&String>,
|
stake_address: Option<&String>,
|
||||||
) -> Result<ShelleyAddress, Error> {
|
) -> Result<ShelleyAddress, Error> {
|
||||||
// Parse stake address
|
// Parse stake address
|
||||||
|
@ -306,44 +306,64 @@ where
|
||||||
};
|
};
|
||||||
|
|
||||||
// Read blueprint
|
// Read blueprint
|
||||||
let filepath = self.blueprint_path();
|
let blueprint = File::open(self.blueprint_path())
|
||||||
let blueprint =
|
.map_err(|_| blueprint::error::Error::InvalidOrMissingFile)?;
|
||||||
File::open(filepath).map_err(|_| blueprint::error::Error::InvalidOrMissingFile)?;
|
|
||||||
let blueprint: Blueprint<serde_json::Value> =
|
let blueprint: Blueprint<serde_json::Value> =
|
||||||
serde_json::from_reader(BufReader::new(blueprint))?;
|
serde_json::from_reader(BufReader::new(blueprint))?;
|
||||||
|
|
||||||
// Find validator's program
|
// Calculate the address
|
||||||
let mut program = None;
|
let when_too_many =
|
||||||
for v in blueprint.validators.iter() {
|
|known_validators| Error::MoreThanOneValidatorFound { known_validators };
|
||||||
if Some(&v.title) == with_title.or(Some(&v.title))
|
let when_missing = |known_validators| Error::NoValidatorNotFound { known_validators };
|
||||||
&& Some(&v.purpose) == with_purpose.or(Some(&v.purpose))
|
blueprint.with_validator(title, purpose, when_too_many, when_missing, |validator| {
|
||||||
{
|
let n = validator.parameters.len();
|
||||||
program = Some(if program.is_none() {
|
if n > 0 {
|
||||||
Ok(v.program.clone())
|
Err(blueprint::error::Error::ParameterizedValidator { n }.into())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::MoreThanOneValidatorFound {
|
Ok(validator
|
||||||
known_validators: blueprint
|
.program
|
||||||
.validators
|
.address(Network::Testnet, delegation_part.to_owned()))
|
||||||
.iter()
|
|
||||||
.map(|v| (v.title.clone(), v.purpose.clone()))
|
|
||||||
.collect(),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print the address
|
pub fn apply_parameter(
|
||||||
match program {
|
&self,
|
||||||
Some(Ok(program)) => Ok(program.address(Network::Testnet, delegation_part)),
|
title: Option<&String>,
|
||||||
Some(Err(e)) => Err(e),
|
purpose: Option<&validator::Purpose>,
|
||||||
None => Err(Error::NoValidatorNotFound {
|
param: &Term<DeBruijn>,
|
||||||
known_validators: blueprint
|
) -> Result<Blueprint<serde_json::Value>, Error> {
|
||||||
|
// Read blueprint
|
||||||
|
let blueprint = File::open(self.blueprint_path())
|
||||||
|
.map_err(|_| blueprint::error::Error::InvalidOrMissingFile)?;
|
||||||
|
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 };
|
||||||
|
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
|
.validators
|
||||||
.iter()
|
.into_iter()
|
||||||
.map(|v| (v.title.clone(), v.purpose.clone()))
|
.map(|validator| {
|
||||||
.collect(),
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_deps(&mut self) -> Result<(), Error> {
|
fn compile_deps(&mut self) -> Result<(), Error> {
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
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
|
||||||
|
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 build;
|
||||||
pub mod check;
|
pub mod check;
|
||||||
pub mod docs;
|
pub mod docs;
|
||||||
|
|
|
@ -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),
|
||||||
|
|
Loading…
Reference in New Issue