Merge pull request #86 from txpipe/54-organize-cli-commands

Organize CLI commands
This commit is contained in:
Lucas 2022-10-28 14:47:45 -04:00 committed by GitHub
commit aae6e2f360
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 581 additions and 498 deletions

23
Cargo.lock generated
View File

@ -61,14 +61,8 @@ dependencies = [
"pallas-crypto",
"pallas-primitives",
"pallas-traverse",
"petgraph",
"regex",
"serde",
"serde_json",
"thiserror",
"toml",
"project",
"uplc",
"walkdir",
]
[[package]]
@ -808,6 +802,21 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "project"
version = "0.0.21"
dependencies = [
"aiken-lang",
"miette",
"petgraph",
"regex",
"serde",
"serde_json",
"thiserror",
"toml",
"walkdir",
]
[[package]]
name = "proptest"
version = "1.0.0"

View File

@ -12,19 +12,14 @@ authors = ["Lucas Rosa <x@rvcas.dev>", "Kasey White <kwhitemsg@gmail.com>"]
anyhow = "1.0.57"
clap = { version = "3.1.14", features = ["derive"] }
hex = "0.4.3"
ignore = "0.4.18"
miette = { version = "5.3.0", features = ["fancy"] }
pallas-addresses = "0.14.0-alpha.3"
pallas-codec = "0.14.0-alpha.3"
pallas-crypto = "0.14.0-alpha.3"
pallas-primitives = "0.14.0-alpha.3"
pallas-traverse = "0.14.0-alpha.3"
serde = { version = "1.0.144", features = ["derive"] }
serde_json = "1.0.85"
uplc = { path = '../uplc', version = "0.0.21" }
aiken-lang = { path = "../lang", version = "0.0.20" }
toml = "0.5.9"
walkdir = "2.3.2"
ignore = "0.4.18"
regex = "1.6.0"
miette = { version = "5.3.0", features = ["fancy"] }
thiserror = "1.0.37"
petgraph = "0.6.2"
project = { path = '../project', version = "0.0.21" }
uplc = { path = '../uplc', version = "0.0.21" }

View File

@ -1,129 +0,0 @@
use std::path::PathBuf;
use clap::{Parser, Subcommand};
/// Cardano smart contract toolchain
#[derive(Parser)]
#[clap(version, about, long_about = None)]
#[clap(propagate_version = true)]
pub enum Args {
/// Build an aiken project
Build {
/// Path to project
#[clap(short, long)]
directory: Option<PathBuf>,
},
/// Typecheck a project project
Check {
/// Path to project
#[clap(short, long)]
directory: Option<PathBuf>,
},
/// Start a development server
Dev,
/// Create a new aiken project
New {
/// Project name
name: PathBuf,
},
/// A subcommand for working with transactions
#[clap(subcommand)]
Tx(TxCommand),
/// A subcommand for working with Untyped Plutus Core
#[clap(subcommand)]
Uplc(UplcCommand),
}
/// Commands for working with transactions
#[derive(Subcommand)]
pub enum TxCommand {
/// Simulate a transaction by evaluating it's script
Simulate {
/// A file containing cbor hex for a transaction
input: PathBuf,
/// Toggle whether input is raw cbor or a hex string
#[clap(short, long)]
cbor: bool,
/// A file containing cbor hex for the raw inputs
raw_inputs: PathBuf,
/// A file containing cbor hex for the raw outputs
raw_outputs: PathBuf,
/// Time between each slot
#[clap(short, long, default_value_t = 1000)]
slot_length: u32,
/// Time of shelley hardfork
#[clap(long, default_value_t = 1596059091000)]
zero_time: u64,
/// Slot number at the start of the shelley hardfork
#[clap(long, default_value_t = 4492800)]
zero_slot: u64,
},
}
/// Commands for working with Untyped Plutus Core
#[derive(Subcommand)]
pub enum UplcCommand {
/// Evaluate an Untyped Plutus Core program
Eval {
script: PathBuf,
#[clap(short, long)]
flat: bool,
/// Arguments to pass to the uplc program
args: Vec<String>,
},
/// Encode textual Untyped Plutus Core to flat bytes
Flat {
/// Textual Untyped Plutus Core file
input: PathBuf,
/// Output file name
#[clap(short, long)]
out: Option<String>,
/// Print output instead of saving to file
#[clap(short, long)]
print: bool,
#[clap(short, long)]
cbor_hex: bool,
},
/// Format an Untyped Plutus Core program
Fmt {
/// Textual Untyped Plutus Core file
input: PathBuf,
/// Print output instead of saving to file
#[clap(short, long)]
print: bool,
},
/// Decode flat bytes to textual Untyped Plutus Core
Unflat {
/// Flat encoded Untyped Plutus Core file
input: PathBuf,
/// Output file name
#[clap(short, long)]
out: Option<String>,
/// Print output instead of saving to file
#[clap(short, long)]
print: bool,
#[clap(short, long)]
cbor_hex: bool,
},
}
impl Default for Args {
fn default() -> Self {
Self::parse()
}
}

View File

@ -0,0 +1,13 @@
use std::path::PathBuf;
#[derive(clap::Args)]
/// Build an Aiken project
pub struct Args {
/// Path to project
#[clap(short, long)]
directory: Option<PathBuf>,
}
pub fn exec(Args { directory }: Args) -> miette::Result<()> {
crate::with_project(directory, |p| p.build())
}

View File

@ -0,0 +1,13 @@
use std::path::PathBuf;
#[derive(clap::Args)]
/// Type-check an Aiken project
pub struct Args {
/// Path to project
#[clap(short, long)]
directory: Option<PathBuf>,
}
pub fn exec(Args { directory }: Args) -> miette::Result<()> {
crate::with_project(directory, |p| p.check())
}

View File

@ -0,0 +1,5 @@
pub mod build;
pub mod check;
pub mod new;
pub mod tx;
pub mod uplc;

20
crates/cli/src/cmd/new.rs Normal file
View File

@ -0,0 +1,20 @@
use miette::IntoDiagnostic;
use std::fs;
use std::path::PathBuf;
#[derive(clap::Args)]
/// Create a new Aiken project
pub struct Args {
/// Project name
name: PathBuf,
}
pub fn exec(Args { name }: Args) -> miette::Result<()> {
if !name.exists() {
fs::create_dir_all(name.join("lib")).into_diagnostic()?;
fs::create_dir_all(name.join("policies")).into_diagnostic()?;
fs::create_dir_all(name.join("scripts")).into_diagnostic()?;
}
Ok(())
}

View File

@ -0,0 +1,16 @@
pub mod simulate;
use clap::Subcommand;
/// Commands for working with transactions
#[derive(Subcommand)]
#[clap(setting(clap::AppSettings::DeriveDisplayOrder))]
pub enum Cmd {
Simulate(simulate::Args),
}
pub fn exec(cmd: Cmd) -> miette::Result<()> {
match cmd {
Cmd::Simulate(args) => simulate::exec(args),
}
}

View File

@ -0,0 +1,125 @@
use miette::IntoDiagnostic;
use pallas_primitives::{
babbage::{TransactionInput, TransactionOutput},
Fragment,
};
use pallas_traverse::{Era, MultiEraTx};
use std::fs;
use std::path::PathBuf;
use uplc::{
machine::cost_model::ExBudget,
tx::{
self,
script_context::{ResolvedInput, SlotConfig},
},
};
#[derive(clap::Args)]
/// Simulate a transaction by evaluating it's script
pub struct Args {
/// A file containing cbor hex for a transaction
input: PathBuf,
/// Toggle whether input is raw cbor or a hex string
#[clap(short, long)]
cbor: bool,
/// A file containing cbor hex for the raw inputs
raw_inputs: PathBuf,
/// A file containing cbor hex for the raw outputs
raw_outputs: PathBuf,
/// Time between each slot
#[clap(short, long, default_value_t = 1000)]
slot_length: u32,
/// Time of shelley hardfork
#[clap(long, default_value_t = 1596059091000)]
zero_time: u64,
/// Slot number at the start of the shelley hardfork
#[clap(long, default_value_t = 4492800)]
zero_slot: u64,
}
pub fn exec(
Args {
input,
cbor,
raw_inputs,
raw_outputs,
slot_length,
zero_time,
zero_slot,
}: Args,
) -> miette::Result<()> {
let (tx_bytes, inputs_bytes, outputs_bytes) = if cbor {
(
fs::read(input).into_diagnostic()?,
fs::read(raw_inputs).into_diagnostic()?,
fs::read(raw_outputs).into_diagnostic()?,
)
} else {
let cbor_hex = fs::read_to_string(input).into_diagnostic()?;
let inputs_hex = fs::read_to_string(raw_inputs).into_diagnostic()?;
let outputs_hex = fs::read_to_string(raw_outputs).into_diagnostic()?;
(
hex::decode(cbor_hex.trim()).into_diagnostic()?,
hex::decode(inputs_hex.trim()).into_diagnostic()?,
hex::decode(outputs_hex.trim()).into_diagnostic()?,
)
};
let tx = MultiEraTx::decode(Era::Babbage, &tx_bytes)
.or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes))
.into_diagnostic()?;
let inputs = Vec::<TransactionInput>::decode_fragment(&inputs_bytes).unwrap();
let outputs = Vec::<TransactionOutput>::decode_fragment(&outputs_bytes).unwrap();
let resolved_inputs: Vec<ResolvedInput> = inputs
.iter()
.zip(outputs.iter())
.map(|(input, output)| ResolvedInput {
input: input.clone(),
output: output.clone(),
})
.collect();
println!("Simulating: {}", tx.hash());
if let Some(tx_babbage) = tx.as_babbage() {
let slot_config = SlotConfig {
zero_time,
zero_slot,
slot_length,
};
let result =
tx::eval_phase_two(tx_babbage, &resolved_inputs, None, None, &slot_config, true);
match result {
Ok(redeemers) => {
println!("\nTotal Budget Used\n-----------------\n");
let total_budget_used =
redeemers
.iter()
.fold(ExBudget { mem: 0, cpu: 0 }, |accum, curr| ExBudget {
mem: accum.mem + curr.ex_units.mem as i64,
cpu: accum.cpu + curr.ex_units.steps as i64,
});
println!("mem: {}", total_budget_used.mem);
println!("cpu: {}", total_budget_used.cpu);
}
Err(err) => {
eprintln!("\nError\n-----\n\n{}\n", err);
}
}
}
Ok(())
}

View File

@ -0,0 +1,75 @@
use miette::IntoDiagnostic;
use std::path::PathBuf;
use uplc::{
ast::{FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term},
machine::cost_model::ExBudget,
parser,
};
#[derive(clap::Args)]
/// Evaluate an Untyped Plutus Core program
pub struct Args {
script: PathBuf,
#[clap(short, long)]
flat: bool,
/// Arguments to pass to the uplc program
args: Vec<String>,
}
pub fn exec(Args { script, flat, args }: Args) -> miette::Result<()> {
let mut program = if flat {
let bytes = std::fs::read(&script).into_diagnostic()?;
let prog = Program::<FakeNamedDeBruijn>::from_flat(&bytes).into_diagnostic()?;
prog.into()
} else {
let code = std::fs::read_to_string(&script).into_diagnostic()?;
let prog = parser::program(&code).into_diagnostic()?;
Program::<NamedDeBruijn>::try_from(prog).into_diagnostic()?
};
for arg in args {
let term: Term<NamedDeBruijn> = parser::term(&arg)
.into_diagnostic()?
.try_into()
.into_diagnostic()?;
program = program.apply_term(&term);
}
let (term, cost, logs) = program.eval();
match term {
Ok(term) => {
let term: Term<Name> = term.try_into().into_diagnostic()?;
println!("\nResult\n------\n\n{}\n", term.to_pretty());
}
Err(err) => {
eprintln!("\nError\n-----\n\n{}\n", err);
}
}
let budget = ExBudget::default();
println!(
"\nCosts\n-----\ncpu: {}\nmemory: {}",
budget.cpu - cost.cpu,
budget.mem - cost.mem
);
println!(
"\nBudget\n------\ncpu: {}\nmemory: {}\n",
cost.cpu, cost.mem
);
if !logs.is_empty() {
println!("\nLogs\n----\n{}", logs.join("\n"))
}
Ok(())
}

View File

@ -0,0 +1,83 @@
use miette::IntoDiagnostic;
use std::{fmt::Write, fs, path::PathBuf};
use uplc::{
ast::{DeBruijn, Program},
parser,
};
#[derive(clap::Args)]
/// Encode textual Untyped Plutus Core to flat bytes
pub struct Args {
/// Textual Untyped Plutus Core file
input: PathBuf,
/// Output file name
#[clap(short, long)]
out: Option<String>,
/// Print output instead of saving to file
#[clap(short, long)]
print: bool,
#[clap(short, long)]
cbor_hex: bool,
}
pub fn exec(
Args {
input,
out,
print,
cbor_hex,
}: Args,
) -> miette::Result<()> {
let code = std::fs::read_to_string(&input).into_diagnostic()?;
let program = parser::program(&code).into_diagnostic()?;
let program = Program::<DeBruijn>::try_from(program).into_diagnostic()?;
if !cbor_hex {
let bytes = program.to_flat().into_diagnostic()?;
if print {
let mut output = String::new();
for (i, byte) in bytes.iter().enumerate() {
let _ = write!(output, "{:08b}", byte);
if (i + 1) % 4 == 0 {
output.push('\n');
} else {
output.push(' ');
}
}
println!("{}", output);
} else {
let out_name = if let Some(out) = out {
out
} else {
format!("{}.flat", input.file_stem().unwrap().to_str().unwrap())
};
fs::write(&out_name, &bytes).into_diagnostic()?;
}
} else {
let cbor = program.to_hex().into_diagnostic()?;
if print {
println!("{}", &cbor);
} else {
let out_name = if let Some(out) = out {
out
} else {
format!("{}.cbor", input.file_stem().unwrap().to_str().unwrap())
};
fs::write(&out_name, &cbor).into_diagnostic()?;
}
}
Ok(())
}

View File

@ -0,0 +1,30 @@
use miette::IntoDiagnostic;
use std::{fs, path::PathBuf};
use uplc::parser;
#[derive(clap::Args)]
/// Format an Untyped Plutus Core program
pub struct Args {
/// Textual Untyped Plutus Core file
input: PathBuf,
/// Print output instead of saving to file
#[clap(short, long)]
print: bool,
}
pub fn exec(Args { input, print }: Args) -> miette::Result<()> {
let code = std::fs::read_to_string(&input).into_diagnostic()?;
let program = parser::program(&code).into_diagnostic()?;
let pretty = program.to_pretty();
if print {
println!("{}", pretty);
} else {
fs::write(&input, pretty).into_diagnostic()?;
}
Ok(())
}

View File

@ -0,0 +1,25 @@
mod eval;
mod flat;
mod fmt;
mod unflat;
use clap::Subcommand;
/// Commands for working with untyped Plutus-core
#[derive(Subcommand)]
#[clap(setting(clap::AppSettings::DeriveDisplayOrder))]
pub enum Cmd {
Fmt(fmt::Args),
Eval(eval::Args),
Flat(flat::Args),
Unflat(unflat::Args),
}
pub fn exec(cmd: Cmd) -> miette::Result<()> {
match cmd {
Cmd::Fmt(args) => fmt::exec(args),
Cmd::Eval(args) => eval::exec(args),
Cmd::Flat(args) => flat::exec(args),
Cmd::Unflat(args) => unflat::exec(args),
}
}

View File

@ -0,0 +1,61 @@
use miette::IntoDiagnostic;
use std::{fs, path::PathBuf};
use uplc::ast::{DeBruijn, Name, Program};
#[derive(clap::Args)]
/// Decode flat bytes to textual Untyped Plutus Core
pub struct Args {
/// Flat encoded Untyped Plutus Core file
input: PathBuf,
/// Output file name
#[clap(short, long)]
out: Option<String>,
/// Print output instead of saving to file
#[clap(short, long)]
print: bool,
#[clap(short, long)]
cbor_hex: bool,
}
pub fn exec(
Args {
input,
out,
print,
cbor_hex,
}: Args,
) -> miette::Result<()> {
let program = if cbor_hex {
let cbor = std::fs::read_to_string(&input).into_diagnostic()?;
let mut cbor_buffer = Vec::new();
let mut flat_buffer = Vec::new();
Program::<DeBruijn>::from_hex(cbor.trim(), &mut cbor_buffer, &mut flat_buffer)
.into_diagnostic()?
} else {
let bytes = std::fs::read(&input).into_diagnostic()?;
Program::<DeBruijn>::from_flat(&bytes).into_diagnostic()?
};
let program: Program<Name> = program.try_into().into_diagnostic()?;
let pretty = program.to_pretty();
if print {
println!("{}", pretty);
} else {
let out_name = if let Some(out) = out {
out
} else {
format!("{}.uplc", input.file_stem().unwrap().to_str().unwrap())
};
fs::write(&out_name, pretty).into_diagnostic()?;
}
Ok(())
}

View File

@ -1,7 +1,41 @@
pub mod config;
pub mod error;
pub mod module;
pub mod project;
pub mod cmd;
pub use aiken_lang;
pub use uplc;
use miette::IntoDiagnostic;
use project::{config::Config, Project};
use std::env;
use std::path::PathBuf;
pub fn with_project<A>(directory: Option<PathBuf>, mut action: A) -> miette::Result<()>
where
A: FnMut(&mut Project) -> Result<(), project::error::Error>,
{
let project_path = if let Some(d) = directory {
d
} else {
env::current_dir().into_diagnostic()?
};
let config = Config::load(project_path.clone()).into_diagnostic()?;
let mut project = Project::new(config, project_path);
let build_result = action(&mut project);
let warning_count = project.warnings.len();
for warning in project.warnings {
warning.report()
}
if let Err(err) = build_result {
err.report();
miette::bail!(
"failed: {} error(s), {warning_count} warning(s)",
err.total(),
);
};
println!("finished with {warning_count} warning(s)");
Ok(())
}

View File

@ -1,355 +1,36 @@
use std::{env, fmt::Write as _, fs};
use aiken::cmd::{build, check, new, tx, uplc};
use clap::Parser;
use miette::IntoDiagnostic;
use pallas_primitives::{
babbage::{TransactionInput, TransactionOutput},
Fragment,
};
use pallas_traverse::{Era, MultiEraTx};
use uplc::{
ast::{DeBruijn, FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term},
machine::cost_model::ExBudget,
parser,
tx::{
self,
script_context::{ResolvedInput, SlotConfig},
},
};
/// Aiken: a smart-contract language and toolchain for Cardano
#[derive(Parser)]
#[clap(version, about, long_about = None)]
#[clap(propagate_version = true)]
#[clap(setting(clap::AppSettings::DeriveDisplayOrder))]
pub enum Cmd {
New(new::Args),
Build(build::Args),
Check(check::Args),
use aiken::{config::Config, project::Project};
#[clap(subcommand)]
Tx(tx::Cmd),
mod args;
#[clap(subcommand)]
Uplc(uplc::Cmd),
}
use args::{Args, TxCommand, UplcCommand};
impl Default for Cmd {
fn default() -> Self {
Self::parse()
}
}
fn main() -> miette::Result<()> {
miette::set_panic_hook();
let args = Args::default();
match args {
Args::Build { directory } => {
let project_path = if let Some(d) = directory {
d
} else {
env::current_dir().into_diagnostic()?
};
let config = Config::load(project_path.clone()).into_diagnostic()?;
let mut project = Project::new(config, project_path);
let build_result = project.build();
let warning_count = project.warnings.len();
for warning in project.warnings {
warning.report()
}
if let Err(err) = build_result {
err.report();
miette::bail!(
"failed: {} error(s), {warning_count} warning(s)",
err.total(),
);
};
println!("finished with {warning_count} warning(s)")
}
Args::Check { directory } => {
let project_path = if let Some(d) = directory {
d
} else {
env::current_dir().into_diagnostic()?
};
let config = Config::load(project_path.clone()).into_diagnostic()?;
let mut project = Project::new(config, project_path);
let build_result = project.check();
let warning_count = project.warnings.len();
for warning in project.warnings {
warning.report()
}
if let Err(err) = build_result {
err.report();
miette::bail!(
"failed: {} error(s), {warning_count} warning(s)",
err.total(),
);
};
println!("finished with {warning_count} warning(s)")
}
Args::Dev => {
// launch a development server
// this should allow people to test
// their contracts over http
todo!()
}
Args::New { name } => {
if !name.exists() {
fs::create_dir_all(name.join("lib")).into_diagnostic()?;
fs::create_dir_all(name.join("policies")).into_diagnostic()?;
fs::create_dir_all(name.join("scripts")).into_diagnostic()?;
match Cmd::default() {
Cmd::New(args) => new::exec(args),
Cmd::Build(args) => build::exec(args),
Cmd::Check(args) => check::exec(args),
Cmd::Tx(sub_cmd) => tx::exec(sub_cmd),
Cmd::Uplc(sub_cmd) => uplc::exec(sub_cmd),
}
}
Args::Tx(tx_cmd) => match tx_cmd {
TxCommand::Simulate {
input,
cbor,
raw_inputs,
raw_outputs,
slot_length,
zero_time,
zero_slot,
} => {
let (tx_bytes, inputs_bytes, outputs_bytes) = if cbor {
(
fs::read(input).into_diagnostic()?,
fs::read(raw_inputs).into_diagnostic()?,
fs::read(raw_outputs).into_diagnostic()?,
)
} else {
let cbor_hex = fs::read_to_string(input).into_diagnostic()?;
let inputs_hex = fs::read_to_string(raw_inputs).into_diagnostic()?;
let outputs_hex = fs::read_to_string(raw_outputs).into_diagnostic()?;
(
hex::decode(cbor_hex.trim()).into_diagnostic()?,
hex::decode(inputs_hex.trim()).into_diagnostic()?,
hex::decode(outputs_hex.trim()).into_diagnostic()?,
)
};
let tx = MultiEraTx::decode(Era::Babbage, &tx_bytes)
.or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes))
.into_diagnostic()?;
let inputs = Vec::<TransactionInput>::decode_fragment(&inputs_bytes).unwrap();
let outputs = Vec::<TransactionOutput>::decode_fragment(&outputs_bytes).unwrap();
let resolved_inputs: Vec<ResolvedInput> = inputs
.iter()
.zip(outputs.iter())
.map(|(input, output)| ResolvedInput {
input: input.clone(),
output: output.clone(),
})
.collect();
println!("Simulating: {}", tx.hash());
if let Some(tx_babbage) = tx.as_babbage() {
let slot_config = SlotConfig {
zero_time,
zero_slot,
slot_length,
};
let result = tx::eval_phase_two(
tx_babbage,
&resolved_inputs,
None,
None,
&slot_config,
true,
);
match result {
Ok(redeemers) => {
println!("\nTotal Budget Used\n-----------------\n");
let total_budget_used = redeemers.iter().fold(
ExBudget { mem: 0, cpu: 0 },
|accum, curr| ExBudget {
mem: accum.mem + curr.ex_units.mem as i64,
cpu: accum.cpu + curr.ex_units.steps as i64,
},
);
println!("mem: {}", total_budget_used.mem);
println!("cpu: {}", total_budget_used.cpu);
}
Err(err) => {
eprintln!("\nError\n-----\n\n{}\n", err);
}
}
}
}
},
Args::Uplc(uplc_cmd) => match uplc_cmd {
UplcCommand::Flat {
input,
print,
out,
cbor_hex,
} => {
let code = std::fs::read_to_string(&input).into_diagnostic()?;
let program = parser::program(&code).into_diagnostic()?;
let program = Program::<DeBruijn>::try_from(program).into_diagnostic()?;
if !cbor_hex {
let bytes = program.to_flat().into_diagnostic()?;
if print {
let mut output = String::new();
for (i, byte) in bytes.iter().enumerate() {
let _ = write!(output, "{:08b}", byte);
if (i + 1) % 4 == 0 {
output.push('\n');
} else {
output.push(' ');
}
}
println!("{}", output);
} else {
let out_name = if let Some(out) = out {
out
} else {
format!("{}.flat", input.file_stem().unwrap().to_str().unwrap())
};
fs::write(&out_name, &bytes).into_diagnostic()?;
}
} else {
let cbor = program.to_hex().into_diagnostic()?;
if print {
println!("{}", &cbor);
} else {
let out_name = if let Some(out) = out {
out
} else {
format!("{}.cbor", input.file_stem().unwrap().to_str().unwrap())
};
fs::write(&out_name, &cbor).into_diagnostic()?;
}
}
}
UplcCommand::Fmt { input, print } => {
let code = std::fs::read_to_string(&input).into_diagnostic()?;
let program = parser::program(&code).into_diagnostic()?;
let pretty = program.to_pretty();
if print {
println!("{}", pretty);
} else {
fs::write(&input, pretty).into_diagnostic()?;
}
}
UplcCommand::Unflat {
input,
print,
out,
cbor_hex,
} => {
let program = if cbor_hex {
let cbor = std::fs::read_to_string(&input).into_diagnostic()?;
let mut cbor_buffer = Vec::new();
let mut flat_buffer = Vec::new();
Program::<DeBruijn>::from_hex(cbor.trim(), &mut cbor_buffer, &mut flat_buffer)
.into_diagnostic()?
} else {
let bytes = std::fs::read(&input).into_diagnostic()?;
Program::<DeBruijn>::from_flat(&bytes).into_diagnostic()?
};
let program: Program<Name> = program.try_into().into_diagnostic()?;
let pretty = program.to_pretty();
if print {
println!("{}", pretty);
} else {
let out_name = if let Some(out) = out {
out
} else {
format!("{}.uplc", input.file_stem().unwrap().to_str().unwrap())
};
fs::write(&out_name, pretty).into_diagnostic()?;
}
}
UplcCommand::Eval { script, flat, args } => {
let mut program = if flat {
let bytes = std::fs::read(&script).into_diagnostic()?;
let prog = Program::<FakeNamedDeBruijn>::from_flat(&bytes).into_diagnostic()?;
prog.into()
} else {
let code = std::fs::read_to_string(&script).into_diagnostic()?;
let prog = parser::program(&code).into_diagnostic()?;
Program::<NamedDeBruijn>::try_from(prog).into_diagnostic()?
};
for arg in args {
let term: Term<NamedDeBruijn> = parser::term(&arg)
.into_diagnostic()?
.try_into()
.into_diagnostic()?;
program = program.apply_term(&term);
}
let (term, cost, logs) = program.eval();
match term {
Ok(term) => {
let term: Term<Name> = term.try_into().into_diagnostic()?;
println!("\nResult\n------\n\n{}\n", term.to_pretty());
}
Err(err) => {
eprintln!("\nError\n-----\n\n{}\n", err);
}
}
let budget = ExBudget::default();
println!(
"\nCosts\n-----\ncpu: {}\nmemory: {}",
budget.cpu - cost.cpu,
budget.mem - cost.mem
);
println!(
"\nBudget\n------\ncpu: {}\nmemory: {}\n",
cost.cpu, cost.mem
);
if !logs.is_empty() {
println!("\nLogs\n----\n{}", logs.join("\n"))
}
}
},
}
Ok(())
}

20
crates/project/Cargo.toml Normal file
View File

@ -0,0 +1,20 @@
[package]
name = "project"
description = "Aiken project utilities"
version = "0.0.21"
edition = "2021"
repository = "https://github.com/txpipe/aiken/crates/project"
homepage = "https://github.com/txpipe/aiken"
license = "Apache-2.0"
authors = ["Lucas Rosa <x@rvcas.dev>", "Kasey White <kwhitemsg@gmail.com>"]
[dependencies]
aiken-lang = { path = "../lang", version = "0.0.20" }
miette = { version = "5.3.0", features = ["fancy"] }
petgraph = "0.6.2"
regex = "1.6.0"
serde = { version = "1.0.144", features = ["derive"] }
serde_json = "1.0.85"
thiserror = "1.0.37"
toml = "0.5.9"
walkdir = "2.3.2"

3
crates/project/README.md Normal file
View File

@ -0,0 +1,3 @@
# Project
This crate encapsulates the code used to manage Aiken projects. See [crates/cli](../cli) for usage.

View File

@ -4,6 +4,10 @@ use std::{
path::{Path, PathBuf},
};
pub mod config;
pub mod error;
pub mod module;
use aiken_lang::{ast::ModuleKind, builtins, tipo::TypeInfo, IdGenerator};
use crate::{