From b55726c90fdbbf1f310744f6a77e03307b0362e0 Mon Sep 17 00:00:00 2001 From: rvcas Date: Fri, 17 Feb 2023 21:56:16 -0500 Subject: [PATCH] feat(project): remove Error::List and use Vec --- crates/aiken-project/src/config.rs | 10 ++-- crates/aiken-project/src/error.rs | 32 ++---------- crates/aiken-project/src/format.rs | 32 ++++++------ crates/aiken-project/src/lib.rs | 69 ++++++++++++++++++------- crates/aiken-project/src/paths.rs | 4 ++ crates/aiken-project/src/telemetry.rs | 4 +- crates/aiken/src/cmd/blueprint/apply.rs | 9 ++-- crates/aiken/src/cmd/fmt.rs | 8 +-- crates/aiken/src/lib.rs | 19 ++++--- 9 files changed, 103 insertions(+), 84 deletions(-) diff --git a/crates/aiken-project/src/config.rs b/crates/aiken-project/src/config.rs index 324bb5fa..56087fdf 100644 --- a/crates/aiken-project/src/config.rs +++ b/crates/aiken-project/src/config.rs @@ -1,10 +1,10 @@ -use crate::{package_name::PackageName, Error}; +use crate::{package_name::PackageName, paths, Error}; use aiken_lang::ast::Span; use miette::NamedSource; use serde::{Deserialize, Serialize}; use std::{fmt::Display, fs, io, path::Path}; -#[derive(Deserialize, Serialize)] +#[derive(Deserialize, Serialize, Clone)] pub struct Config { pub name: PackageName, pub version: String, @@ -16,7 +16,7 @@ pub struct Config { pub dependencies: Vec, } -#[derive(Deserialize, Serialize)] +#[derive(Deserialize, Serialize, Clone)] pub struct Repository { pub user: String, pub project: String, @@ -72,13 +72,13 @@ impl Config { } pub fn save(&self, dir: &Path) -> Result<(), io::Error> { - let aiken_toml_path = dir.join("aiken.toml"); + let aiken_toml_path = dir.join(paths::project_config()); let aiken_toml = toml::to_string_pretty(self).unwrap(); fs::write(aiken_toml_path, aiken_toml) } pub fn load(dir: &Path) -> Result { - let config_path = dir.join("aiken.toml"); + let config_path = dir.join(paths::project_config()); let raw_config = fs::read_to_string(&config_path).map_err(|_| Error::MissingManifest { path: dir.to_path_buf(), })?; diff --git a/crates/aiken-project/src/error.rs b/crates/aiken-project/src/error.rs index cb136de0..0016b458 100644 --- a/crates/aiken-project/src/error.rs +++ b/crates/aiken-project/src/error.rs @@ -69,10 +69,6 @@ pub enum Error { #[error("I just found a cycle in module hierarchy!")] ImportCycle { modules: Vec }, - /// Useful for returning many [`Error::Parse`] at once - #[error("A list of errors")] - List(Vec), - #[error("While parsing files...")] Parse { path: PathBuf, @@ -139,25 +135,11 @@ pub enum Error { } impl Error { - pub fn len(&self) -> usize { - match self { - Error::List(errors) => errors.len(), - _ => 1, - } - } - pub fn report(&self) { - match self { - Error::List(errors) => { - for error in errors { - eprintln!("Error: {error:?}") - } - } - rest => eprintln!("Error: {rest:?}"), - } + eprintln!("Error: {self:?}") } - pub fn from_parse_errors(errs: Vec, path: &Path, src: &str) -> Self { + pub fn from_parse_errors(errs: Vec, path: &Path, src: &str) -> Vec { let mut errors = Vec::with_capacity(errs.len()); for error in errs { @@ -209,7 +191,6 @@ impl Error { Error::MissingManifest { path } => Some(path.to_path_buf()), Error::TomlLoading { path, .. } => Some(path.to_path_buf()), Error::ImportCycle { .. } => None, - Error::List(_) => None, Error::Parse { path, .. } => Some(path.to_path_buf()), Error::Type { path, .. } => Some(path.to_path_buf()), Error::ValidatorMustReturnBool { path, .. } => Some(path.to_path_buf()), @@ -226,7 +207,7 @@ impl Error { } } - pub fn src(&self) -> Option { + fn src(&self) -> Option { match self { Error::DuplicateModule { .. } => None, Error::FileIo { .. } => None, @@ -236,7 +217,6 @@ impl Error { Error::MissingManifest { .. } => None, Error::TomlLoading { src, .. } => Some(src.to_string()), Error::ImportCycle { .. } => None, - Error::List(_) => None, Error::Parse { src, .. } => Some(src.to_string()), Error::Type { src, .. } => Some(src.to_string()), Error::ValidatorMustReturnBool { src, .. } => Some(src.to_string()), @@ -284,7 +264,6 @@ impl Diagnostic for Error { Error::FileIo { .. } => None, Error::Blueprint(e) => e.code(), Error::ImportCycle { .. } => Some(Box::new("aiken::module::cyclical")), - Error::List(_) => None, Error::Parse { .. } => Some(Box::new("aiken::parser")), Error::Type { error, .. } => Some(Box::new(format!( "aiken::check{}", @@ -321,7 +300,6 @@ impl Diagnostic for Error { "Try moving the shared code to a separate module that the others can depend on\n- {}", modules.join("\n- ") ))), - Error::List(_) => None, Error::Parse { error, .. } => error.kind.help(), Error::Type { error, .. } => error.help(), Error::StandardIo(_) => None, @@ -388,7 +366,6 @@ impl Diagnostic for Error { Error::FileIo { .. } => None, Error::ImportCycle { .. } => None, Error::Blueprint(e) => e.labels(), - Error::List(_) => None, Error::Parse { error, .. } => error.labels(), Error::MissingManifest { .. } => None, Error::Type { error, .. } => error.labels(), @@ -427,7 +404,6 @@ impl Diagnostic for Error { Error::FileIo { .. } => None, Error::ImportCycle { .. } => None, Error::Blueprint(e) => e.source_code(), - Error::List(_) => None, Error::Parse { named, .. } => Some(named), Error::Type { named, .. } => Some(named), Error::StandardIo(_) => None, @@ -454,7 +430,6 @@ impl Diagnostic for Error { Error::FileIo { .. } => None, Error::ImportCycle { .. } => None, Error::Blueprint(e) => e.url(), - Error::List { .. } => None, Error::Parse { .. } => None, Error::Type { error, .. } => error.url(), Error::StandardIo(_) => None, @@ -481,7 +456,6 @@ impl Diagnostic for Error { Error::FileIo { .. } => None, Error::Blueprint(e) => e.related(), Error::ImportCycle { .. } => None, - Error::List { .. } => None, Error::Parse { .. } => None, Error::Type { error, .. } => error.related(), Error::StandardIo(_) => None, diff --git a/crates/aiken-project/src/format.rs b/crates/aiken-project/src/format.rs index afc2d93c..00a4278f 100644 --- a/crates/aiken-project/src/format.rs +++ b/crates/aiken-project/src/format.rs @@ -12,7 +12,7 @@ use crate::{ is_aiken_path, }; -pub fn run(stdin: bool, check: bool, files: Vec) -> Result<(), Error> { +pub fn run(stdin: bool, check: bool, files: Vec) -> Result<(), Vec> { if stdin { process_stdin(check) } else { @@ -20,7 +20,7 @@ pub fn run(stdin: bool, check: bool, files: Vec) -> Result<(), Error> { } } -fn process_stdin(check: bool) -> Result<(), Error> { +fn process_stdin(check: bool) -> Result<(), Vec> { let src = read_stdin()?; let mut out = String::new(); @@ -36,20 +36,20 @@ fn process_stdin(check: bool) -> Result<(), Error> { } if src != out { - return Err(Error::Format { + return Err(vec![Error::Format { problem_files: vec![Unformatted { source: PathBuf::from(""), destination: PathBuf::from(""), input: src, output: out, }], - }); + }]); } Ok(()) } -fn process_files(check: bool, files: Vec) -> Result<(), Error> { +fn process_files(check: bool, files: Vec) -> Result<(), Vec> { if check { check_files(files) } else { @@ -57,39 +57,39 @@ fn process_files(check: bool, files: Vec) -> Result<(), Error> { } } -fn check_files(files: Vec) -> Result<(), Error> { +fn check_files(files: Vec) -> Result<(), Vec> { let problem_files = unformatted_files(files)?; if problem_files.is_empty() { Ok(()) } else { - Err(Error::Format { problem_files }) + Err(Error::Format { problem_files }.into()) } } -fn format_files(files: Vec) -> Result<(), Error> { +fn format_files(files: Vec) -> Result<(), Vec> { for file in unformatted_files(files)? { - fs::write(file.destination, file.output)?; + fs::write(file.destination, file.output).map_err(Error::from)?; } Ok(()) } -fn unformatted_files(files: Vec) -> Result, Error> { +fn unformatted_files(files: Vec) -> Result, Vec> { let mut problem_files = Vec::with_capacity(files.len()); - let mut errors = Error::List(vec![]); + let mut errors = vec![]; for file_path in files { let path = PathBuf::from_str(&file_path).unwrap(); if path.is_dir() { for path in aiken_files_excluding_gitignore(&path) { - if let Err(err) = format_file(&mut problem_files, path) { - errors = errors.append(err); + if let Err(mut errs) = format_file(&mut problem_files, path) { + errors.append(&mut errs); }; } - } else if let Err(err) = format_file(&mut problem_files, path) { - errors = errors.append(err); + } else if let Err(mut errs) = format_file(&mut problem_files, path) { + errors.append(&mut errs); } } @@ -100,7 +100,7 @@ fn unformatted_files(files: Vec) -> Result, Error> { } } -fn format_file(problem_files: &mut Vec, path: PathBuf) -> Result<(), Error> { +fn format_file(problem_files: &mut Vec, path: PathBuf) -> Result<(), Vec> { let src = fs::read_to_string(&path).map_err(|error| Error::FileIo { error, path: path.clone(), diff --git a/crates/aiken-project/src/lib.rs b/crates/aiken-project/src/lib.rs index 51f5f4f5..cf8bc57a 100644 --- a/crates/aiken-project/src/lib.rs +++ b/crates/aiken-project/src/lib.rs @@ -67,7 +67,7 @@ where module_types: HashMap, root: PathBuf, sources: Vec, - pub warnings: Vec, + warnings: Vec, event_listener: T, functions: IndexMap, data_types: IndexMap, @@ -78,6 +78,14 @@ where T: EventListener, { pub fn new(root: PathBuf, event_listener: T) -> Result, Error> { + let config = Config::load(&root)?; + + let project = Project::new_with_config(config, root, event_listener); + + Ok(project) + } + + pub fn new_with_config(config: Config, root: PathBuf, event_listener: T) -> Project { let id_gen = IdGenerator::new(); let mut module_types = HashMap::new(); @@ -89,9 +97,7 @@ where let data_types = builtins::prelude_data_types(&id_gen); - let config = Config::load(&root)?; - - Ok(Project { + Project { config, checked_modules: CheckedModules::default(), defined_modules: HashMap::new(), @@ -103,10 +109,30 @@ where event_listener, functions, data_types, - }) + } } - pub fn build(&mut self, uplc: bool, tracing: Tracing) -> Result<(), Error> { + pub fn warnings(&mut self) -> Vec { + std::mem::take(&mut self.warnings) + } + + pub fn modules(&self) -> Vec { + self.checked_modules.values().cloned().collect() + } + + pub fn checkpoint(&self) -> Checkpoint { + Checkpoint { + module_types: self.module_types.clone(), + defined_modules: self.defined_modules.clone(), + } + } + + pub fn restore(&mut self, checkpoint: Checkpoint) { + self.module_types = checkpoint.module_types; + self.defined_modules = checkpoint.defined_modules; + } + + pub fn build(&mut self, uplc: bool, tracing: Tracing) -> Result<(), Vec> { let options = Options { code_gen_mode: CodeGenMode::Build(uplc), tracing, @@ -115,7 +141,7 @@ where self.compile(options) } - pub fn docs(&mut self, destination: Option) -> Result<(), Error> { + pub fn docs(&mut self, destination: Option) -> Result<(), Vec> { self.compile_deps()?; self.event_listener @@ -145,8 +171,8 @@ where for file in doc_files { let path = destination.join(file.path); - fs::create_dir_all(path.parent().unwrap())?; - fs::write(&path, file.content)?; + fs::create_dir_all(path.parent().unwrap()).map_err(Error::from)?; + fs::write(&path, file.content).map_err(Error::from)?; } Ok(()) @@ -159,7 +185,7 @@ where verbose: bool, exact_match: bool, tracing: Tracing, - ) -> Result<(), Error> { + ) -> Result<(), Vec> { let options = Options { tracing, code_gen_mode: if skip_tests { @@ -198,7 +224,7 @@ where self.root.join("plutus.json") } - pub fn compile(&mut self, options: Options) -> Result<(), Error> { + pub fn compile(&mut self, options: Options) -> Result<(), Vec> { self.compile_deps()?; self.event_listener @@ -239,9 +265,13 @@ where } let json = serde_json::to_string_pretty(&blueprint).unwrap(); - fs::write(self.blueprint_path(), json).map_err(|error| Error::FileIo { - error, - path: self.blueprint_path(), + + fs::write(self.blueprint_path(), json).map_err(|error| { + Error::FileIo { + error, + path: self.blueprint_path(), + } + .into() }) } CodeGenMode::Test { @@ -278,7 +308,7 @@ where .handle_event(Event::FinishedTests { tests: results }); if !errors.is_empty() { - Err(Error::List(errors)) + Err(errors) } else { Ok(()) } @@ -372,7 +402,7 @@ where Ok(blueprint) } - fn compile_deps(&mut self) -> Result<(), Error> { + fn compile_deps(&mut self) -> Result<(), Vec> { let manifest = deps::download( &self.event_listener, UseManifest::Yes, @@ -416,7 +446,7 @@ where Ok(()) } - fn parse_sources(&mut self, package_name: PackageName) -> Result { + fn parse_sources(&mut self, package_name: PackageName) -> Result> { let mut errors = Vec::new(); let mut parsed_modules = HashMap::with_capacity(self.sources.len()); @@ -450,7 +480,8 @@ where module: module.name.clone(), first, second: module.path, - }); + } + .into()); } module.attach_doc_and_module_comments(); @@ -473,7 +504,7 @@ where if errors.is_empty() { Ok(parsed_modules.into()) } else { - Err(Error::List(errors)) + Err(errors) } } diff --git a/crates/aiken-project/src/paths.rs b/crates/aiken-project/src/paths.rs index 2d2af521..0b9f50b2 100644 --- a/crates/aiken-project/src/paths.rs +++ b/crates/aiken-project/src/paths.rs @@ -3,6 +3,10 @@ use crate::{error::Error, package_name::PackageName}; use reqwest::Client; use std::path::PathBuf; +pub fn project_config() -> PathBuf { + PathBuf::from("aiken.toml") +} + pub fn manifest() -> PathBuf { PathBuf::from("aiken.lock") } diff --git a/crates/aiken-project/src/telemetry.rs b/crates/aiken-project/src/telemetry.rs index 3143ea3f..fb3def6f 100644 --- a/crates/aiken-project/src/telemetry.rs +++ b/crates/aiken-project/src/telemetry.rs @@ -1,8 +1,8 @@ use crate::script::EvalInfo; use std::path::PathBuf; -pub trait EventListener: std::fmt::Debug { - fn handle_event(&self, event: Event); +pub trait EventListener { + fn handle_event(&self, _event: Event) {} } pub enum Event { diff --git a/crates/aiken/src/cmd/blueprint/apply.rs b/crates/aiken/src/cmd/blueprint/apply.rs index 8d736457..8a611b6a 100644 --- a/crates/aiken/src/cmd/blueprint/apply.rs +++ b/crates/aiken/src/cmd/blueprint/apply.rs @@ -56,9 +56,12 @@ pub fn exec( 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(), + fs::write(p.blueprint_path(), json).map_err(|error| { + Error::FileIo { + error, + path: p.blueprint_path(), + } + .into() }) }) } diff --git a/crates/aiken/src/cmd/fmt.rs b/crates/aiken/src/cmd/fmt.rs index e2bcc3bf..5246e3f2 100644 --- a/crates/aiken/src/cmd/fmt.rs +++ b/crates/aiken/src/cmd/fmt.rs @@ -21,10 +21,12 @@ pub fn exec( files, }: Args, ) -> miette::Result<()> { - if let Err(err) = aiken_project::format::run(stdin, check, files) { - err.report(); + if let Err(errs) = aiken_project::format::run(stdin, check, files) { + for err in &errs { + err.report(); + } - miette::bail!("failed: {} error(s)", err.len()); + miette::bail!("failed: {} error(s)", errs.len()); }; Ok(()) diff --git a/crates/aiken/src/lib.rs b/crates/aiken/src/lib.rs index 192b98c8..4ed860ea 100644 --- a/crates/aiken/src/lib.rs +++ b/crates/aiken/src/lib.rs @@ -8,7 +8,7 @@ pub mod cmd; pub fn with_project(directory: Option, mut action: A) -> miette::Result<()> where - A: FnMut(&mut Project) -> Result<(), aiken_project::error::Error>, + A: FnMut(&mut Project) -> Result<(), Vec>, { let project_path = if let Some(d) = directory { d @@ -26,23 +26,28 @@ where let build_result = action(&mut project); - let warning_count = project.warnings.len(); + let warnings = project.warnings(); - for warning in project.warnings { + let warning_count = warnings.len(); + + for warning in warnings { warning.report() } let plural = if warning_count == 1 { "" } else { "s" }; - if let Err(err) = build_result { - err.report(); + if let Err(errs) = build_result { + for err in &errs { + err.report() + } + println!("\n{}", "Summary".purple().bold()); let warning_text = format!("{warning_count} warning{plural}"); - let plural = if err.len() == 1 { "" } else { "s" }; + let plural = if errs.len() == 1 { "" } else { "s" }; - let error_text = format!("{} error{}", err.len(), plural); + let error_text = format!("{} error{}", errs.len(), plural); let full_summary = format!(" {}, {}", error_text.red(), warning_text.yellow());