feat(project): remove Error::List and use Vec<Error>

This commit is contained in:
rvcas 2023-02-17 21:56:16 -05:00 committed by Lucas
parent 70164282f8
commit b55726c90f
9 changed files with 103 additions and 84 deletions

View File

@ -1,10 +1,10 @@
use crate::{package_name::PackageName, Error}; use crate::{package_name::PackageName, paths, Error};
use aiken_lang::ast::Span; use aiken_lang::ast::Span;
use miette::NamedSource; use miette::NamedSource;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{fmt::Display, fs, io, path::Path}; use std::{fmt::Display, fs, io, path::Path};
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize, Clone)]
pub struct Config { pub struct Config {
pub name: PackageName, pub name: PackageName,
pub version: String, pub version: String,
@ -16,7 +16,7 @@ pub struct Config {
pub dependencies: Vec<Dependency>, pub dependencies: Vec<Dependency>,
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize, Clone)]
pub struct Repository { pub struct Repository {
pub user: String, pub user: String,
pub project: String, pub project: String,
@ -72,13 +72,13 @@ impl Config {
} }
pub fn save(&self, dir: &Path) -> Result<(), io::Error> { 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(); let aiken_toml = toml::to_string_pretty(self).unwrap();
fs::write(aiken_toml_path, aiken_toml) fs::write(aiken_toml_path, aiken_toml)
} }
pub fn load(dir: &Path) -> Result<Config, Error> { pub fn load(dir: &Path) -> Result<Config, Error> {
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 { let raw_config = fs::read_to_string(&config_path).map_err(|_| Error::MissingManifest {
path: dir.to_path_buf(), path: dir.to_path_buf(),
})?; })?;

View File

@ -69,10 +69,6 @@ pub enum Error {
#[error("I just found a cycle in module hierarchy!")] #[error("I just found a cycle in module hierarchy!")]
ImportCycle { modules: Vec<String> }, ImportCycle { modules: Vec<String> },
/// Useful for returning many [`Error::Parse`] at once
#[error("A list of errors")]
List(Vec<Self>),
#[error("While parsing files...")] #[error("While parsing files...")]
Parse { Parse {
path: PathBuf, path: PathBuf,
@ -139,25 +135,11 @@ pub enum Error {
} }
impl Error { impl Error {
pub fn len(&self) -> usize {
match self {
Error::List(errors) => errors.len(),
_ => 1,
}
}
pub fn report(&self) { pub fn report(&self) {
match self { eprintln!("Error: {self:?}")
Error::List(errors) => {
for error in errors {
eprintln!("Error: {error:?}")
}
}
rest => eprintln!("Error: {rest:?}"),
}
} }
pub fn from_parse_errors(errs: Vec<ParseError>, path: &Path, src: &str) -> Self { pub fn from_parse_errors(errs: Vec<ParseError>, path: &Path, src: &str) -> Vec<Self> {
let mut errors = Vec::with_capacity(errs.len()); let mut errors = Vec::with_capacity(errs.len());
for error in errs { for error in errs {
@ -209,7 +191,6 @@ impl Error {
Error::MissingManifest { path } => Some(path.to_path_buf()), Error::MissingManifest { path } => Some(path.to_path_buf()),
Error::TomlLoading { path, .. } => Some(path.to_path_buf()), Error::TomlLoading { path, .. } => Some(path.to_path_buf()),
Error::ImportCycle { .. } => None, Error::ImportCycle { .. } => None,
Error::List(_) => None,
Error::Parse { path, .. } => Some(path.to_path_buf()), Error::Parse { path, .. } => Some(path.to_path_buf()),
Error::Type { path, .. } => Some(path.to_path_buf()), Error::Type { path, .. } => Some(path.to_path_buf()),
Error::ValidatorMustReturnBool { 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<String> { fn src(&self) -> Option<String> {
match self { match self {
Error::DuplicateModule { .. } => None, Error::DuplicateModule { .. } => None,
Error::FileIo { .. } => None, Error::FileIo { .. } => None,
@ -236,7 +217,6 @@ impl Error {
Error::MissingManifest { .. } => None, Error::MissingManifest { .. } => None,
Error::TomlLoading { src, .. } => Some(src.to_string()), Error::TomlLoading { src, .. } => Some(src.to_string()),
Error::ImportCycle { .. } => None, Error::ImportCycle { .. } => None,
Error::List(_) => None,
Error::Parse { src, .. } => Some(src.to_string()), Error::Parse { src, .. } => Some(src.to_string()),
Error::Type { src, .. } => Some(src.to_string()), Error::Type { src, .. } => Some(src.to_string()),
Error::ValidatorMustReturnBool { src, .. } => Some(src.to_string()), Error::ValidatorMustReturnBool { src, .. } => Some(src.to_string()),
@ -284,7 +264,6 @@ impl Diagnostic for Error {
Error::FileIo { .. } => None, Error::FileIo { .. } => None,
Error::Blueprint(e) => e.code(), Error::Blueprint(e) => e.code(),
Error::ImportCycle { .. } => Some(Box::new("aiken::module::cyclical")), Error::ImportCycle { .. } => Some(Box::new("aiken::module::cyclical")),
Error::List(_) => None,
Error::Parse { .. } => Some(Box::new("aiken::parser")), Error::Parse { .. } => Some(Box::new("aiken::parser")),
Error::Type { error, .. } => Some(Box::new(format!( Error::Type { error, .. } => Some(Box::new(format!(
"aiken::check{}", "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- {}", "Try moving the shared code to a separate module that the others can depend on\n- {}",
modules.join("\n- ") modules.join("\n- ")
))), ))),
Error::List(_) => None,
Error::Parse { error, .. } => error.kind.help(), Error::Parse { error, .. } => error.kind.help(),
Error::Type { error, .. } => error.help(), Error::Type { error, .. } => error.help(),
Error::StandardIo(_) => None, Error::StandardIo(_) => None,
@ -388,7 +366,6 @@ impl Diagnostic for Error {
Error::FileIo { .. } => None, Error::FileIo { .. } => None,
Error::ImportCycle { .. } => None, Error::ImportCycle { .. } => None,
Error::Blueprint(e) => e.labels(), Error::Blueprint(e) => e.labels(),
Error::List(_) => None,
Error::Parse { error, .. } => error.labels(), Error::Parse { error, .. } => error.labels(),
Error::MissingManifest { .. } => None, Error::MissingManifest { .. } => None,
Error::Type { error, .. } => error.labels(), Error::Type { error, .. } => error.labels(),
@ -427,7 +404,6 @@ impl Diagnostic for Error {
Error::FileIo { .. } => None, Error::FileIo { .. } => None,
Error::ImportCycle { .. } => None, Error::ImportCycle { .. } => None,
Error::Blueprint(e) => e.source_code(), Error::Blueprint(e) => e.source_code(),
Error::List(_) => None,
Error::Parse { named, .. } => Some(named), Error::Parse { named, .. } => Some(named),
Error::Type { named, .. } => Some(named), Error::Type { named, .. } => Some(named),
Error::StandardIo(_) => None, Error::StandardIo(_) => None,
@ -454,7 +430,6 @@ impl Diagnostic for Error {
Error::FileIo { .. } => None, Error::FileIo { .. } => None,
Error::ImportCycle { .. } => None, Error::ImportCycle { .. } => None,
Error::Blueprint(e) => e.url(), Error::Blueprint(e) => e.url(),
Error::List { .. } => None,
Error::Parse { .. } => None, Error::Parse { .. } => None,
Error::Type { error, .. } => error.url(), Error::Type { error, .. } => error.url(),
Error::StandardIo(_) => None, Error::StandardIo(_) => None,
@ -481,7 +456,6 @@ impl Diagnostic for Error {
Error::FileIo { .. } => None, Error::FileIo { .. } => None,
Error::Blueprint(e) => e.related(), Error::Blueprint(e) => e.related(),
Error::ImportCycle { .. } => None, Error::ImportCycle { .. } => None,
Error::List { .. } => None,
Error::Parse { .. } => None, Error::Parse { .. } => None,
Error::Type { error, .. } => error.related(), Error::Type { error, .. } => error.related(),
Error::StandardIo(_) => None, Error::StandardIo(_) => None,

View File

@ -12,7 +12,7 @@ use crate::{
is_aiken_path, is_aiken_path,
}; };
pub fn run(stdin: bool, check: bool, files: Vec<String>) -> Result<(), Error> { pub fn run(stdin: bool, check: bool, files: Vec<String>) -> Result<(), Vec<Error>> {
if stdin { if stdin {
process_stdin(check) process_stdin(check)
} else { } else {
@ -20,7 +20,7 @@ pub fn run(stdin: bool, check: bool, files: Vec<String>) -> Result<(), Error> {
} }
} }
fn process_stdin(check: bool) -> Result<(), Error> { fn process_stdin(check: bool) -> Result<(), Vec<Error>> {
let src = read_stdin()?; let src = read_stdin()?;
let mut out = String::new(); let mut out = String::new();
@ -36,20 +36,20 @@ fn process_stdin(check: bool) -> Result<(), Error> {
} }
if src != out { if src != out {
return Err(Error::Format { return Err(vec![Error::Format {
problem_files: vec![Unformatted { problem_files: vec![Unformatted {
source: PathBuf::from("<standard input>"), source: PathBuf::from("<standard input>"),
destination: PathBuf::from("<standard output>"), destination: PathBuf::from("<standard output>"),
input: src, input: src,
output: out, output: out,
}], }],
}); }]);
} }
Ok(()) Ok(())
} }
fn process_files(check: bool, files: Vec<String>) -> Result<(), Error> { fn process_files(check: bool, files: Vec<String>) -> Result<(), Vec<Error>> {
if check { if check {
check_files(files) check_files(files)
} else { } else {
@ -57,39 +57,39 @@ fn process_files(check: bool, files: Vec<String>) -> Result<(), Error> {
} }
} }
fn check_files(files: Vec<String>) -> Result<(), Error> { fn check_files(files: Vec<String>) -> Result<(), Vec<Error>> {
let problem_files = unformatted_files(files)?; let problem_files = unformatted_files(files)?;
if problem_files.is_empty() { if problem_files.is_empty() {
Ok(()) Ok(())
} else { } else {
Err(Error::Format { problem_files }) Err(Error::Format { problem_files }.into())
} }
} }
fn format_files(files: Vec<String>) -> Result<(), Error> { fn format_files(files: Vec<String>) -> Result<(), Vec<Error>> {
for file in unformatted_files(files)? { for file in unformatted_files(files)? {
fs::write(file.destination, file.output)?; fs::write(file.destination, file.output).map_err(Error::from)?;
} }
Ok(()) Ok(())
} }
fn unformatted_files(files: Vec<String>) -> Result<Vec<Unformatted>, Error> { fn unformatted_files(files: Vec<String>) -> Result<Vec<Unformatted>, Vec<Error>> {
let mut problem_files = Vec::with_capacity(files.len()); let mut problem_files = Vec::with_capacity(files.len());
let mut errors = Error::List(vec![]); let mut errors = vec![];
for file_path in files { for file_path in files {
let path = PathBuf::from_str(&file_path).unwrap(); let path = PathBuf::from_str(&file_path).unwrap();
if path.is_dir() { if path.is_dir() {
for path in aiken_files_excluding_gitignore(&path) { for path in aiken_files_excluding_gitignore(&path) {
if let Err(err) = format_file(&mut problem_files, path) { if let Err(mut errs) = format_file(&mut problem_files, path) {
errors = errors.append(err); errors.append(&mut errs);
}; };
} }
} else if let Err(err) = format_file(&mut problem_files, path) { } else if let Err(mut errs) = format_file(&mut problem_files, path) {
errors = errors.append(err); errors.append(&mut errs);
} }
} }
@ -100,7 +100,7 @@ fn unformatted_files(files: Vec<String>) -> Result<Vec<Unformatted>, Error> {
} }
} }
fn format_file(problem_files: &mut Vec<Unformatted>, path: PathBuf) -> Result<(), Error> { fn format_file(problem_files: &mut Vec<Unformatted>, path: PathBuf) -> Result<(), Vec<Error>> {
let src = fs::read_to_string(&path).map_err(|error| Error::FileIo { let src = fs::read_to_string(&path).map_err(|error| Error::FileIo {
error, error,
path: path.clone(), path: path.clone(),

View File

@ -67,7 +67,7 @@ where
module_types: HashMap<String, TypeInfo>, module_types: HashMap<String, TypeInfo>,
root: PathBuf, root: PathBuf,
sources: Vec<Source>, sources: Vec<Source>,
pub warnings: Vec<Warning>, warnings: Vec<Warning>,
event_listener: T, event_listener: T,
functions: IndexMap<FunctionAccessKey, TypedFunction>, functions: IndexMap<FunctionAccessKey, TypedFunction>,
data_types: IndexMap<DataTypeKey, TypedDataType>, data_types: IndexMap<DataTypeKey, TypedDataType>,
@ -78,6 +78,14 @@ where
T: EventListener, T: EventListener,
{ {
pub fn new(root: PathBuf, event_listener: T) -> Result<Project<T>, Error> { pub fn new(root: PathBuf, event_listener: T) -> Result<Project<T>, 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<T> {
let id_gen = IdGenerator::new(); let id_gen = IdGenerator::new();
let mut module_types = HashMap::new(); let mut module_types = HashMap::new();
@ -89,9 +97,7 @@ where
let data_types = builtins::prelude_data_types(&id_gen); let data_types = builtins::prelude_data_types(&id_gen);
let config = Config::load(&root)?; Project {
Ok(Project {
config, config,
checked_modules: CheckedModules::default(), checked_modules: CheckedModules::default(),
defined_modules: HashMap::new(), defined_modules: HashMap::new(),
@ -103,10 +109,30 @@ where
event_listener, event_listener,
functions, functions,
data_types, data_types,
}) }
} }
pub fn build(&mut self, uplc: bool, tracing: Tracing) -> Result<(), Error> { pub fn warnings(&mut self) -> Vec<Warning> {
std::mem::take(&mut self.warnings)
}
pub fn modules(&self) -> Vec<CheckedModule> {
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<Error>> {
let options = Options { let options = Options {
code_gen_mode: CodeGenMode::Build(uplc), code_gen_mode: CodeGenMode::Build(uplc),
tracing, tracing,
@ -115,7 +141,7 @@ where
self.compile(options) self.compile(options)
} }
pub fn docs(&mut self, destination: Option<PathBuf>) -> Result<(), Error> { pub fn docs(&mut self, destination: Option<PathBuf>) -> Result<(), Vec<Error>> {
self.compile_deps()?; self.compile_deps()?;
self.event_listener self.event_listener
@ -145,8 +171,8 @@ where
for file in doc_files { for file in doc_files {
let path = destination.join(file.path); let path = destination.join(file.path);
fs::create_dir_all(path.parent().unwrap())?; fs::create_dir_all(path.parent().unwrap()).map_err(Error::from)?;
fs::write(&path, file.content)?; fs::write(&path, file.content).map_err(Error::from)?;
} }
Ok(()) Ok(())
@ -159,7 +185,7 @@ where
verbose: bool, verbose: bool,
exact_match: bool, exact_match: bool,
tracing: Tracing, tracing: Tracing,
) -> Result<(), Error> { ) -> Result<(), Vec<Error>> {
let options = Options { let options = Options {
tracing, tracing,
code_gen_mode: if skip_tests { code_gen_mode: if skip_tests {
@ -198,7 +224,7 @@ where
self.root.join("plutus.json") self.root.join("plutus.json")
} }
pub fn compile(&mut self, options: Options) -> Result<(), Error> { pub fn compile(&mut self, options: Options) -> Result<(), Vec<Error>> {
self.compile_deps()?; self.compile_deps()?;
self.event_listener self.event_listener
@ -239,9 +265,13 @@ where
} }
let json = serde_json::to_string_pretty(&blueprint).unwrap(); let json = serde_json::to_string_pretty(&blueprint).unwrap();
fs::write(self.blueprint_path(), json).map_err(|error| Error::FileIo {
fs::write(self.blueprint_path(), json).map_err(|error| {
Error::FileIo {
error, error,
path: self.blueprint_path(), path: self.blueprint_path(),
}
.into()
}) })
} }
CodeGenMode::Test { CodeGenMode::Test {
@ -278,7 +308,7 @@ where
.handle_event(Event::FinishedTests { tests: results }); .handle_event(Event::FinishedTests { tests: results });
if !errors.is_empty() { if !errors.is_empty() {
Err(Error::List(errors)) Err(errors)
} else { } else {
Ok(()) Ok(())
} }
@ -372,7 +402,7 @@ where
Ok(blueprint) Ok(blueprint)
} }
fn compile_deps(&mut self) -> Result<(), Error> { fn compile_deps(&mut self) -> Result<(), Vec<Error>> {
let manifest = deps::download( let manifest = deps::download(
&self.event_listener, &self.event_listener,
UseManifest::Yes, UseManifest::Yes,
@ -416,7 +446,7 @@ where
Ok(()) Ok(())
} }
fn parse_sources(&mut self, package_name: PackageName) -> Result<ParsedModules, Error> { fn parse_sources(&mut self, package_name: PackageName) -> Result<ParsedModules, Vec<Error>> {
let mut errors = Vec::new(); let mut errors = Vec::new();
let mut parsed_modules = HashMap::with_capacity(self.sources.len()); let mut parsed_modules = HashMap::with_capacity(self.sources.len());
@ -450,7 +480,8 @@ where
module: module.name.clone(), module: module.name.clone(),
first, first,
second: module.path, second: module.path,
}); }
.into());
} }
module.attach_doc_and_module_comments(); module.attach_doc_and_module_comments();
@ -473,7 +504,7 @@ where
if errors.is_empty() { if errors.is_empty() {
Ok(parsed_modules.into()) Ok(parsed_modules.into())
} else { } else {
Err(Error::List(errors)) Err(errors)
} }
} }

View File

@ -3,6 +3,10 @@ use crate::{error::Error, package_name::PackageName};
use reqwest::Client; use reqwest::Client;
use std::path::PathBuf; use std::path::PathBuf;
pub fn project_config() -> PathBuf {
PathBuf::from("aiken.toml")
}
pub fn manifest() -> PathBuf { pub fn manifest() -> PathBuf {
PathBuf::from("aiken.lock") PathBuf::from("aiken.lock")
} }

View File

@ -1,8 +1,8 @@
use crate::script::EvalInfo; use crate::script::EvalInfo;
use std::path::PathBuf; use std::path::PathBuf;
pub trait EventListener: std::fmt::Debug { pub trait EventListener {
fn handle_event(&self, event: Event); fn handle_event(&self, _event: Event) {}
} }
pub enum Event { pub enum Event {

View File

@ -56,9 +56,12 @@ pub fn exec(
let json = serde_json::to_string_pretty(&blueprint).unwrap(); let json = serde_json::to_string_pretty(&blueprint).unwrap();
fs::write(p.blueprint_path(), json).map_err(|error| Error::FileIo { fs::write(p.blueprint_path(), json).map_err(|error| {
Error::FileIo {
error, error,
path: p.blueprint_path(), path: p.blueprint_path(),
}
.into()
}) })
}) })
} }

View File

@ -21,10 +21,12 @@ pub fn exec(
files, files,
}: Args, }: Args,
) -> miette::Result<()> { ) -> miette::Result<()> {
if let Err(err) = aiken_project::format::run(stdin, check, files) { if let Err(errs) = aiken_project::format::run(stdin, check, files) {
for err in &errs {
err.report(); err.report();
}
miette::bail!("failed: {} error(s)", err.len()); miette::bail!("failed: {} error(s)", errs.len());
}; };
Ok(()) Ok(())

View File

@ -8,7 +8,7 @@ pub mod cmd;
pub fn with_project<A>(directory: Option<PathBuf>, mut action: A) -> miette::Result<()> pub fn with_project<A>(directory: Option<PathBuf>, mut action: A) -> miette::Result<()>
where where
A: FnMut(&mut Project<Terminal>) -> Result<(), aiken_project::error::Error>, A: FnMut(&mut Project<Terminal>) -> Result<(), Vec<aiken_project::error::Error>>,
{ {
let project_path = if let Some(d) = directory { let project_path = if let Some(d) = directory {
d d
@ -26,23 +26,28 @@ where
let build_result = action(&mut project); 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() warning.report()
} }
let plural = if warning_count == 1 { "" } else { "s" }; let plural = if warning_count == 1 { "" } else { "s" };
if let Err(err) = build_result { if let Err(errs) = build_result {
err.report(); for err in &errs {
err.report()
}
println!("\n{}", "Summary".purple().bold()); println!("\n{}", "Summary".purple().bold());
let warning_text = format!("{warning_count} warning{plural}"); 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()); let full_summary = format!(" {}, {}", error_text.red(), warning_text.yellow());