feat: start to report errors instead of panicing

This commit is contained in:
YetAnotherMinion 2021-09-16 06:59:57 +01:00 committed by nobody
commit 601137dd16
Signed by: GrocerPublishAgent
GPG key ID: D460CD54A9E3AB86
3 changed files with 95 additions and 38 deletions

View file

@ -12,6 +12,7 @@ uuid = { version = "0.8", features = [ "v4" ] }
ahash = "0.7" ahash = "0.7"
serde = { version = "1.0", features = [ "derive" ] } serde = { version = "1.0", features = [ "derive" ] }
serde_json = { version ="1.0", features = [] } serde_json = { version ="1.0", features = [] }
naive-wadler-prettier= { path = "../../../infra/redwood-lang/compiler/naive-wadler-prettier" }
# All of these are required for deno's javascript runtime. We need to keep the # All of these are required for deno's javascript runtime. We need to keep the
# same versions as other projects in our cargo workspace. Multiple different # same versions as other projects in our cargo workspace. Multiple different

View file

@ -1,3 +1,5 @@
extern crate naive_wadler_prettier as pretty;
use crate::reporting::{Problem, SetupError, TypeError};
use elmi::DataBinary; use elmi::DataBinary;
use rusty_v8 as v8; use rusty_v8 as v8;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -16,6 +18,8 @@ use structopt::StructOpt;
use tokio; use tokio;
use uuid::Uuid; use uuid::Uuid;
mod reporting;
fn main() { fn main() {
let args = Arguments::from_args(); let args = Arguments::from_args();
@ -67,9 +71,11 @@ fn main() {
} }
// TODO remove this unwrap // TODO remove this unwrap
// TODO CompilerError::
let data = std::fs::read(&file).unwrap(); let data = std::fs::read(&file).unwrap();
let mut hasher = PortableHash::new(); let mut hasher = PortableHash::new();
// TODO CompilerError::ElmJsonChecksumFailed(io::Error, PathBuf),
hasher.write_all(&data).unwrap(); hasher.write_all(&data).unwrap();
// also include the function name in the checksum so users can run multiple functions // also include the function name in the checksum so users can run multiple functions
// from the same file but still get caching if the file does not change. // from the same file but still get caching if the file does not change.
@ -497,7 +503,7 @@ enum OutputType {
Value, Value,
} }
fn resolve_function_type(tipe: &elmi::Type) -> Result<(Option<InputType>, OutputType), Problem> { fn resolve_function_type(tipe: &elmi::Type) -> Result<(Option<InputType>, OutputType), TypeError> {
match tipe { match tipe {
elmi::Type::TLambda(a, b) => { elmi::Type::TLambda(a, b) => {
// We want to check the output types first because this is where we will figure out if // We want to check the output types first because this is where we will figure out if
@ -507,7 +513,7 @@ fn resolve_function_type(tipe: &elmi::Type) -> Result<(Option<InputType>, Output
Ok((Some(input_type), output_type)) Ok((Some(input_type), output_type))
} }
elmi::Type::TVar(_) => Err(Problem::CantEvalGeneric), elmi::Type::TVar(_) => Err(TypeError::CantEvalGeneric),
elmi::Type::TType(module_name, name, args) if args.is_empty() => { elmi::Type::TType(module_name, name, args) if args.is_empty() => {
// If our function returns a primitive type // If our function returns a primitive type
if module_name == "elm/core/String" && name == "String" { if module_name == "elm/core/String" && name == "String" {
@ -517,12 +523,12 @@ fn resolve_function_type(tipe: &elmi::Type) -> Result<(Option<InputType>, Output
return Ok((None, OutputType::String)); return Ok((None, OutputType::String));
} }
Err(Problem::CantEvalType(tipe.clone())) Err(TypeError::CantEvalType(tipe.clone()))
} }
elmi::Type::TType(module_name, name, args) => Err(Problem::CantEvalCustomType), elmi::Type::TType(module_name, name, args) => Err(TypeError::CantEvalCustomType),
elmi::Type::TRecord(_, _) => Err(Problem::CantEvalRecord), elmi::Type::TRecord(_, _) => Err(TypeError::CantEvalRecord),
elmi::Type::TUnit => Err(Problem::CantEvalUnit), elmi::Type::TUnit => Err(TypeError::CantEvalUnit),
elmi::Type::TTuple(_, _, _) => Err(Problem::CantEvalTuple), elmi::Type::TTuple(_, _, _) => Err(TypeError::CantEvalTuple),
elmi::Type::TAlias(_, _, _, ref alias) => { elmi::Type::TAlias(_, _, _, ref alias) => {
match &**alias { match &**alias {
elmi::AliasType::Filled(tipe) => { elmi::AliasType::Filled(tipe) => {
@ -539,15 +545,15 @@ fn resolve_function_type(tipe: &elmi::Type) -> Result<(Option<InputType>, Output
// ``` // ```
resolve_function_type(tipe) resolve_function_type(tipe)
} }
elmi::AliasType::Holey(_) => return Err(Problem::CantEvalHoleyAlias), elmi::AliasType::Holey(_) => return Err(TypeError::CantEvalHoleyAlias),
} }
} }
} }
} }
fn resolve_input_type(tipe: &elmi::Type) -> Result<InputType, Problem> { fn resolve_input_type(tipe: &elmi::Type) -> Result<InputType, TypeError> {
match tipe { match tipe {
elmi::Type::TLambda(_, _) => Err(Problem::EvalRequiresSingleArgument(tipe.clone())), elmi::Type::TLambda(_, _) => Err(TypeError::EvalRequiresSingleArgument(tipe.clone())),
elmi::Type::TType(module_name, name, args) if args.is_empty() => { elmi::Type::TType(module_name, name, args) if args.is_empty() => {
if module_name == "elm/core/String" && name == "String" { if module_name == "elm/core/String" && name == "String" {
Ok(InputType::String) Ok(InputType::String)
@ -556,18 +562,18 @@ fn resolve_input_type(tipe: &elmi::Type) -> Result<InputType, Problem> {
} else if module_name == "elm/json/Json.Encode" && name == "Value" { } else if module_name == "elm/json/Json.Encode" && name == "Value" {
Ok(InputType::Value) Ok(InputType::Value)
} else { } else {
Err(Problem::InputTypeNotSupported(tipe.clone())) Err(TypeError::InputTypeNotSupported(tipe.clone()))
} }
} }
elmi::Type::TAlias(_, _, _, ref alias) => match &**alias { elmi::Type::TAlias(_, _, _, ref alias) => match &**alias {
elmi::AliasType::Filled(tipe) => resolve_input_type(tipe), elmi::AliasType::Filled(tipe) => resolve_input_type(tipe),
elmi::AliasType::Holey(_) => Err(Problem::CantEvalHoleyAlias), elmi::AliasType::Holey(_) => Err(TypeError::CantEvalHoleyAlias),
}, },
_ => Err(Problem::OutputTypeNotSupported(tipe.clone())), _ => Err(TypeError::OutputTypeNotSupported(tipe.clone())),
} }
} }
fn resolve_output_type(tipe: &elmi::Type) -> Result<OutputType, Problem> { fn resolve_output_type(tipe: &elmi::Type) -> Result<OutputType, TypeError> {
match tipe { match tipe {
elmi::Type::TType(module_name, name, args) => { elmi::Type::TType(module_name, name, args) => {
if module_name == "elm/core/String" && name == "String" { if module_name == "elm/core/String" && name == "String" {
@ -579,33 +585,17 @@ fn resolve_output_type(tipe: &elmi::Type) -> Result<OutputType, Problem> {
} else if module_name == "elm/virtual-dom/VirtualDom" && name == "Node" { } else if module_name == "elm/virtual-dom/VirtualDom" && name == "Node" {
Ok(OutputType::Html) Ok(OutputType::Html)
} else { } else {
Err(Problem::OutputTypeNotSupported(tipe.clone())) Err(TypeError::OutputTypeNotSupported(tipe.clone()))
} }
} }
elmi::Type::TAlias(_, _, _, ref alias) => match &**alias { elmi::Type::TAlias(_, _, _, ref alias) => match &**alias {
elmi::AliasType::Filled(tipe) => resolve_output_type(tipe), elmi::AliasType::Filled(tipe) => resolve_output_type(tipe),
elmi::AliasType::Holey(_) => Err(Problem::CantEvalHoleyAlias), elmi::AliasType::Holey(_) => Err(TypeError::CantEvalHoleyAlias),
}, },
_ => Err(Problem::OutputTypeNotSupported(tipe.clone())), _ => Err(TypeError::OutputTypeNotSupported(tipe.clone())),
} }
} }
#[derive(Debug, Clone)]
enum Problem {
CantEvalRecord,
CantEvalUnit,
CantEvalCustomType,
CantEvalHoleyAlias,
CantEvalTuple,
CantEvalGeneric,
CantEvalType(elmi::Type),
InputTypeNotSupported(elmi::Type),
OutputTypeNotSupported(elmi::Type),
EvalRequiresSingleArgument(elmi::Type),
InstallDependencyFailed,
}
fn setup_generator_project(elm_project_dir: PathBuf) -> Result<PathBuf, Problem> { fn setup_generator_project(elm_project_dir: PathBuf) -> Result<PathBuf, Problem> {
// I added a couple of random bytes to the directory name to reduce the risk of // I added a couple of random bytes to the directory name to reduce the risk of
// collisions with other programs that also use elm-stuff for their scratch space // collisions with other programs that also use elm-stuff for their scratch space
@ -707,11 +697,11 @@ fn elm_install<P: AsRef<Path>, S: AsRef<str>>(our_temp_dir: P, package: S) -> Re
match child.wait() { match child.wait() {
Ok(exit_status) => { Ok(exit_status) => {
if !exit_status.success() { if !exit_status.success() {
return Err(Problem::InstallDependencyFailed); return Err(SetupError::InstallDependencyFailed.into());
} }
} }
Err(_) => { Err(_) => {
return Err(Problem::InstallDependencyFailed); return Err(SetupError::InstallDependencyFailed.into());
} }
} }
@ -725,9 +715,10 @@ fn generate_fixture<S: AsRef<str>>(
input: Option<InputType>, input: Option<InputType>,
output: OutputType, output: OutputType,
) -> (String, String) { ) -> (String, String) {
// TODO update this size with a better estimate once I know how large the completed file is // The generated fixture Elm files are around 3007 bytes which includes the import of the
// likely to be. // target module and function
let mut buffer = String::with_capacity(4096); let mut buffer =
String::with_capacity(3000 + target_module.as_ref().len() + target_function.as_ref().len());
let module_name = format!("Gen{:020}", source_checksum); let module_name = format!("Gen{:020}", source_checksum);

65
src/reporting.rs Normal file
View file

@ -0,0 +1,65 @@
use deno_core::error::{type_error, AnyError};
use elmi;
use pretty::{self, hang, hardline, hcat, hsep, sep, space_join, Doc};
use rusty_v8;
use std::io;
use std::path::PathBuf;
#[derive(Debug)]
pub enum Problem {
BadTypes(TypeError),
BadCompiler(CompilerError),
BadSetup(SetupError),
BadRuntime(InterpreterError),
}
impl From<TypeError> for Problem {
fn from(error: TypeError) -> Self {
Self::BadTypes(error)
}
}
impl From<CompilerError> for Problem {
fn from(error: CompilerError) -> Self {
Self::BadCompiler(error)
}
}
impl From<SetupError> for Problem {
fn from(error: SetupError) -> Self {
Self::BadSetup(error)
}
}
#[derive(Debug)]
pub enum TypeError {
CantEvalRecord,
CantEvalUnit,
CantEvalCustomType,
CantEvalHoleyAlias,
CantEvalTuple,
CantEvalGeneric,
CantEvalType(elmi::Type),
InputTypeNotSupported(elmi::Type),
OutputTypeNotSupported(elmi::Type),
EvalRequiresSingleArgument(elmi::Type),
}
#[derive(Debug)]
pub enum SetupError {
InstallDependencyFailed,
}
#[derive(Debug)]
pub enum CompilerError {
ElmJsonChecksumFailed(io::Error, PathBuf),
ReadInputFailed(io::Error, PathBuf),
WriteOutputFailed(io::Error, PathBuf),
FailedElmiParse(String),
}
#[derive(Debug)]
pub enum InterpreterError {
Setup(rusty_v8::DataError),
EventLoop(AnyError),
}