feat(cli): rework uplc subcommands

closes #553

* rename flat to encode
* rename unflat to decode
* alias both to their old names
* both only print to stdout
  use can pipe to file
* split cbor and hex flags
* hex flag works for either cbor or flat
* encode takes --to flag
  [name, named-debruijn, debruijn]
* decode takes --from flag
  [name, named-debruijn, debruijn]
This commit is contained in:
rvcas
2023-06-01 23:50:59 -04:00
parent e5d9398625
commit 335cc0c8bc
8 changed files with 181 additions and 154 deletions

View File

@@ -0,0 +1,78 @@
use miette::IntoDiagnostic;
use std::{path::PathBuf, println};
use uplc::ast::{DeBruijn, Name, NamedDeBruijn, Program};
use super::Format;
#[derive(clap::Args)]
/// Decode flat bytes to textual Untyped Plutus Core
pub struct Args {
/// Flat encoded Untyped Plutus Core file
input: PathBuf,
// Format to convert from
#[clap(long, default_value = "debruijn")]
from: Format,
/// Input file contains cbor encoded flat bytes
#[clap(short, long)]
cbor: bool,
/// Input file contents will be hex decoded
#[clap(long)]
hex: bool,
}
pub fn exec(
Args {
input,
from,
cbor,
hex,
}: Args,
) -> miette::Result<()> {
let bytes = if hex {
let hex_bytes = std::fs::read_to_string(&input).into_diagnostic()?;
hex::decode(hex_bytes).into_diagnostic()?
} else {
std::fs::read(&input).into_diagnostic()?
};
let pretty_uplc = match from {
Format::Name => {
let program: Program<Name> = if cbor {
let mut flat_buffer = Vec::new();
Program::from_cbor(&bytes, &mut flat_buffer).into_diagnostic()?
} else {
Program::from_flat(&bytes).into_diagnostic()?
};
program.to_pretty()
}
Format::NamedDebruijn => {
let program: Program<NamedDeBruijn> = if cbor {
let mut flat_buffer = Vec::new();
Program::from_cbor(&bytes, &mut flat_buffer).into_diagnostic()?
} else {
Program::from_flat(&bytes).into_diagnostic()?
};
program.to_pretty()
}
Format::Debruijn => {
let program: Program<DeBruijn> = if cbor {
let mut flat_buffer = Vec::new();
Program::from_cbor(&bytes, &mut flat_buffer).into_diagnostic()?
} else {
Program::from_flat(&bytes).into_diagnostic()?
};
program.to_pretty()
}
};
println!("{pretty_uplc}");
Ok(())
}

View File

@@ -0,0 +1,83 @@
use miette::IntoDiagnostic;
use std::{
io::{self, Write},
path::PathBuf,
};
use uplc::{
ast::{DeBruijn, NamedDeBruijn, Program},
flat::Binder,
parser,
};
use super::Format;
/// Encode textual Untyped Plutus Core to flat bytes
#[derive(clap::Args)]
pub struct Args {
/// Textual Untyped Plutus Core file
input: PathBuf,
// Format to convert to
#[clap(long, default_value = "debruijn")]
to: Format,
/// Further encode the flat bytes as cbor bytes
#[clap(short, long)]
cbor: bool,
/// Hex encode the bytes
#[clap(long)]
hex: bool,
}
pub fn exec(
Args {
input,
to,
cbor,
hex,
}: Args,
) -> miette::Result<()> {
let code = std::fs::read_to_string(input).into_diagnostic()?;
let program = parser::program(&code).into_diagnostic()?;
match to {
Format::Name => encode(program, cbor, hex),
Format::NamedDebruijn => {
let program: Program<NamedDeBruijn> = program.try_into().into_diagnostic()?;
encode(program, cbor, hex)
}
Format::Debruijn => {
let program: Program<DeBruijn> = program.try_into().into_diagnostic()?;
encode(program, cbor, hex)
}
}
}
fn encode<'a, T>(program: Program<T>, cbor: bool, hex: bool) -> miette::Result<()>
where
T: Binder<'a> + std::fmt::Debug,
{
let mut stdout = io::stdout();
let bytes = if cbor {
program.to_cbor().into_diagnostic()?
} else {
program.to_flat().into_diagnostic()?
};
if hex {
let bytes_hex = hex::encode(bytes);
print!("{bytes_hex}");
} else {
stdout.write_all(&bytes).into_diagnostic()?;
}
stdout.flush().into_diagnostic()?;
Ok(())
}

View File

@@ -1,83 +0,0 @@
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, "{byte:08b}");
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

@@ -1,24 +1,33 @@
mod decode;
mod encode;
mod eval;
mod flat;
mod fmt;
mod unflat;
use clap::Subcommand;
use clap::{Subcommand, ValueEnum};
#[derive(Copy, Clone, ValueEnum)]
pub(super) enum Format {
Name,
NamedDebruijn,
Debruijn,
}
/// Commands for working with untyped Plutus-core
#[derive(Subcommand)]
pub enum Cmd {
Fmt(fmt::Args),
Eval(eval::Args),
Flat(flat::Args),
Unflat(unflat::Args),
#[clap(alias = "flat")]
Encode(encode::Args),
#[clap(alias = "unflat")]
Decode(decode::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),
Cmd::Encode(args) => encode::exec(args),
Cmd::Decode(args) => decode::exec(args),
}
}

View File

@@ -1,61 +0,0 @@
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(())
}