refactor: move prints to cli via EventListener trait

This commit is contained in:
rvcas 2022-12-08 19:23:50 -05:00
parent 801ab3989e
commit 4ad74bef1f
No known key found for this signature in database
GPG Key ID: C09B64E263F7D68C
6 changed files with 158 additions and 89 deletions

2
Cargo.lock generated
View File

@ -59,6 +59,7 @@ dependencies = [
"ignore",
"indoc",
"miette",
"owo-colors",
"pallas-addresses",
"pallas-codec",
"pallas-crypto",
@ -110,7 +111,6 @@ dependencies = [
"hex",
"ignore",
"miette",
"owo-colors",
"pallas",
"pallas-traverse",
"petgraph",

View File

@ -15,6 +15,7 @@ hex = "0.4.3"
ignore = "0.4.18"
indoc = "1.0"
miette = { version = "5.3.0", features = ["fancy"] }
owo-colors = "3.5.0"
pallas-addresses = "0.14.0"
pallas-codec = "0.14.0"
pallas-crypto = "0.14.0"

View File

@ -1,13 +1,19 @@
pub mod cmd;
use std::{env, path::PathBuf};
use aiken_project::{config::Config, Project};
use aiken_project::{
config::Config,
telemetry::{self, TestInfo},
Project,
};
use miette::IntoDiagnostic;
use std::env;
use std::path::PathBuf;
use owo_colors::OwoColorize;
use uplc::machine::cost_model::ExBudget;
pub mod cmd;
pub fn with_project<A>(directory: Option<PathBuf>, mut action: A) -> miette::Result<()>
where
A: FnMut(&mut Project) -> Result<(), aiken_project::error::Error>,
A: FnMut(&mut Project<Terminal>) -> Result<(), aiken_project::error::Error>,
{
let project_path = if let Some(d) = directory {
d
@ -17,7 +23,7 @@ where
let config = Config::load(project_path.clone()).into_diagnostic()?;
let mut project = Project::new(config, project_path);
let mut project = Project::new(config, project_path, Terminal::default());
let build_result = action(&mut project);
@ -37,3 +43,97 @@ where
Ok(())
}
#[derive(Debug, Default, Clone, Copy)]
pub struct Terminal;
impl telemetry::EventListener for Terminal {
fn handle_event(&self, event: telemetry::Event) {
match event {
telemetry::Event::CompilingPackage { .. } => todo!(),
telemetry::Event::RunningTests => {
println!("\n{}\n", "Running tests...".bold().underline().purple());
}
telemetry::Event::FinishedTests { tests } => {
let (max_mem, max_cpu) = tests.iter().fold(
(0, 0),
|(max_mem, max_cpu), TestInfo { spent_budget, .. }| {
if spent_budget.mem >= max_mem && spent_budget.cpu >= max_cpu {
(spent_budget.mem, spent_budget.cpu)
} else if spent_budget.mem > max_mem {
(spent_budget.mem, max_cpu)
} else if spent_budget.cpu > max_cpu {
(max_mem, spent_budget.cpu)
} else {
(max_mem, max_cpu)
}
},
);
let max_mem = max_mem.to_string().len() as i32;
let max_cpu = max_cpu.to_string().len() as i32;
for test_info in &tests {
println!("{}", fmt_test(test_info, max_mem, max_cpu))
}
let (n_passed, n_failed) =
tests
.iter()
.fold((0, 0), |(n_passed, n_failed), test_info| {
if test_info.is_passing {
(n_passed + 1, n_failed)
} else {
(n_passed, n_failed + 1)
}
});
println!(
"{}",
format!(
"\n Summary: {} test(s), {}; {}.",
tests.len(),
format!("{} passed", n_passed).bright_green(),
format!("{} failed", n_failed).bright_red()
)
.bold()
)
}
}
}
}
fn fmt_test(test_info: &TestInfo, max_mem: i32, max_cpu: i32) -> String {
let TestInfo {
is_passing,
test,
spent_budget,
} = test_info;
let ExBudget { mem, cpu } = spent_budget;
format!(
" [{}] [mem: {}, cpu: {}] {}::{}",
if *is_passing {
"PASS".bold().green().to_string()
} else {
"FAIL".bold().red().to_string()
},
pad_left(mem.to_string(), max_mem, " "),
pad_left(cpu.to_string(), max_cpu, " "),
test.module.blue(),
test.name.bright_blue()
)
}
fn pad_left(mut text: String, n: i32, delimiter: &str) -> String {
let diff = n - text.len() as i32;
if diff.is_positive() {
for _ in 0..diff {
text.insert_str(0, delimiter);
}
}
text
}

View File

@ -23,4 +23,3 @@ walkdir = "2.3.2"
hex = "0.4.3"
pallas = "0.14.0"
pallas-traverse = "0.14.0"
owo-colors = "3.5.0"

View File

@ -9,6 +9,7 @@ pub mod error;
pub mod format;
pub mod module;
pub mod script;
pub mod telemetry;
use aiken_lang::{
ast::{Definition, Function, ModuleKind, TypedFunction},
@ -18,7 +19,6 @@ use aiken_lang::{
IdGenerator,
};
use miette::NamedSource;
use owo_colors::OwoColorize;
use pallas::{
codec::minicbor,
ledger::{addresses::Address, primitives::babbage},
@ -26,6 +26,7 @@ use pallas::{
use pallas_traverse::ComputeHash;
use script::Script;
use serde_json::json;
use telemetry::{EventListener, TestInfo};
use uplc::{
ast::{DeBruijn, Program},
machine::cost_model::ExBudget,
@ -35,6 +36,7 @@ use crate::{
config::Config,
error::{Error, Warning},
module::{CheckedModule, CheckedModules, ParsedModule, ParsedModules},
telemetry::Event,
};
#[derive(Debug)]
@ -51,7 +53,10 @@ pub const MINT: &str = "mint";
pub const WITHDRAWL: &str = "withdrawl";
pub const VALIDATOR_NAMES: [&str; 4] = [SPEND, CERT, MINT, WITHDRAWL];
pub struct Project {
pub struct Project<T>
where
T: EventListener,
{
config: Config,
defined_modules: HashMap<String, PathBuf>,
id_gen: IdGenerator,
@ -59,10 +64,14 @@ pub struct Project {
root: PathBuf,
sources: Vec<Source>,
pub warnings: Vec<Warning>,
event_listener: T,
}
impl Project {
pub fn new(config: Config, root: PathBuf) -> Project {
impl<T> Project<T>
where
T: EventListener,
{
pub fn new(config: Config, root: PathBuf, event_listener: T) -> Project<T> {
let id_gen = IdGenerator::new();
let mut module_types = HashMap::new();
@ -78,6 +87,7 @@ impl Project {
root,
sources: vec![],
warnings: vec![],
event_listener,
}
}
@ -492,7 +502,7 @@ impl Project {
};
if !tests.is_empty() {
println!("\n{}\n", "Running tests...".bold().underline().purple());
self.event_listener.handle_event(Event::RunningTests);
}
let mut results = Vec::new();
@ -500,75 +510,28 @@ impl Project {
for test in tests {
match test.program.eval(initial_budget) {
(Ok(..), remaining_budget, _) => {
results.push((true, test, initial_budget - remaining_budget));
// println!("{}", fmt_tests);
let test_info = TestInfo {
is_passing: true,
test,
spent_budget: initial_budget - remaining_budget,
};
results.push(test_info);
}
(Err(_), remaining_budget, _) => {
results.push((false, test, initial_budget - remaining_budget));
// println!("{}", fmt_tests());
let test_info = TestInfo {
is_passing: false,
test,
spent_budget: initial_budget - remaining_budget,
};
results.push(test_info);
}
}
}
let (max_mem, max_cpu) =
results
.iter()
.fold((0, 0), |(max_mem, max_cpu), (_, _, budget)| {
if budget.mem >= max_mem && budget.cpu >= max_cpu {
(budget.mem, budget.cpu)
} else if budget.mem > max_mem {
(budget.mem, max_cpu)
} else if budget.cpu > max_cpu {
(max_mem, budget.cpu)
} else {
(max_mem, max_cpu)
}
});
let max_mem = max_mem.to_string().len() as i32;
let max_cpu = max_cpu.to_string().len() as i32;
let fmt_tests = |is_passing: &bool, test: &Script, spent_budget: &ExBudget| -> String {
let ExBudget { mem, cpu } = spent_budget;
format!(
" [{}] [mem: {}, cpu: {}] {}::{}",
if *is_passing {
"PASS".bold().green().to_string()
} else {
"FAIL".bold().red().to_string()
},
pad_left(mem.to_string(), max_mem, " "),
pad_left(cpu.to_string(), max_cpu, " "),
test.module.blue(),
test.name.bright_blue()
)
};
for (is_passing, test, spent_budget) in &results {
println!("{}", fmt_tests(is_passing, test, spent_budget))
}
let (n_passed, n_failed) =
results
.iter()
.fold((0, 0), |(n_passed, n_failed), (is_passing, _, _)| {
if *is_passing {
(n_passed + 1, n_failed)
} else {
(n_passed, n_failed + 1)
}
});
println!(
"{}",
format!(
"\n Summary: {} test(s), {}; {}.",
results.len(),
format!("{} passed", n_passed).bright_green(),
format!("{} failed", n_failed).bright_red()
)
.bold()
)
self.event_listener
.handle_event(Event::FinishedTests { tests: results });
}
fn write_build_outputs(&self, programs: Vec<Script>, uplc_dump: bool) -> Result<(), Error> {
@ -727,16 +690,3 @@ fn is_aiken_path(path: &Path, dir: impl AsRef<Path>) -> bool {
.expect("is_aiken_path(): to_str"),
)
}
fn pad_left(text: String, n: i32, delimiter: &str) -> String {
let mut text = text.clone();
let diff = n - text.len() as i32;
if diff.is_positive() {
for _ in 0..diff {
text.insert_str(0, delimiter);
}
}
text
}

View File

@ -0,0 +1,19 @@
use uplc::machine::cost_model::ExBudget;
use crate::script::Script;
pub trait EventListener: std::fmt::Debug {
fn handle_event(&self, event: Event);
}
pub enum Event {
CompilingPackage { name: String },
RunningTests,
FinishedTests { tests: Vec<TestInfo> },
}
pub struct TestInfo {
pub is_passing: bool,
pub test: Script,
pub spent_budget: ExBudget,
}