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"
serde = { version = "1.0", features = [ "derive" ] }
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
# 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 rusty_v8 as v8;
use serde::{Deserialize, Serialize};
@ -16,6 +18,8 @@ use structopt::StructOpt;
use tokio;
use uuid::Uuid;
mod reporting;
fn main() {
let args = Arguments::from_args();
@ -67,9 +71,11 @@ fn main() {
}
// TODO remove this unwrap
// TODO CompilerError::
let data = std::fs::read(&file).unwrap();
let mut hasher = PortableHash::new();
// TODO CompilerError::ElmJsonChecksumFailed(io::Error, PathBuf),
hasher.write_all(&data).unwrap();
// 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.
@ -497,7 +503,7 @@ enum OutputType {
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 {
elmi::Type::TLambda(a, b) => {
// 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))
}
elmi::Type::TVar(_) => Err(Problem::CantEvalGeneric),
elmi::Type::TVar(_) => Err(TypeError::CantEvalGeneric),
elmi::Type::TType(module_name, name, args) if args.is_empty() => {
// If our function returns a primitive type
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));
}
Err(Problem::CantEvalType(tipe.clone()))
Err(TypeError::CantEvalType(tipe.clone()))
}
elmi::Type::TType(module_name, name, args) => Err(Problem::CantEvalCustomType),
elmi::Type::TRecord(_, _) => Err(Problem::CantEvalRecord),
elmi::Type::TUnit => Err(Problem::CantEvalUnit),
elmi::Type::TTuple(_, _, _) => Err(Problem::CantEvalTuple),
elmi::Type::TType(module_name, name, args) => Err(TypeError::CantEvalCustomType),
elmi::Type::TRecord(_, _) => Err(TypeError::CantEvalRecord),
elmi::Type::TUnit => Err(TypeError::CantEvalUnit),
elmi::Type::TTuple(_, _, _) => Err(TypeError::CantEvalTuple),
elmi::Type::TAlias(_, _, _, ref alias) => {
match &**alias {
elmi::AliasType::Filled(tipe) => {
@ -539,15 +545,15 @@ fn resolve_function_type(tipe: &elmi::Type) -> Result<(Option<InputType>, Output
// ```
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 {
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() => {
if module_name == "elm/core/String" && name == "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" {
Ok(InputType::Value)
} else {
Err(Problem::InputTypeNotSupported(tipe.clone()))
Err(TypeError::InputTypeNotSupported(tipe.clone()))
}
}
elmi::Type::TAlias(_, _, _, ref alias) => match &**alias {
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 {
elmi::Type::TType(module_name, name, args) => {
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" {
Ok(OutputType::Html)
} else {
Err(Problem::OutputTypeNotSupported(tipe.clone()))
Err(TypeError::OutputTypeNotSupported(tipe.clone()))
}
}
elmi::Type::TAlias(_, _, _, ref alias) => match &**alias {
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> {
// 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
@ -707,11 +697,11 @@ fn elm_install<P: AsRef<Path>, S: AsRef<str>>(our_temp_dir: P, package: S) -> Re
match child.wait() {
Ok(exit_status) => {
if !exit_status.success() {
return Err(Problem::InstallDependencyFailed);
return Err(SetupError::InstallDependencyFailed.into());
}
}
Err(_) => {
return Err(Problem::InstallDependencyFailed);
return Err(SetupError::InstallDependencyFailed.into());
}
}
@ -725,9 +715,10 @@ fn generate_fixture<S: AsRef<str>>(
input: Option<InputType>,
output: OutputType,
) -> (String, String) {
// TODO update this size with a better estimate once I know how large the completed file is
// likely to be.
let mut buffer = String::with_capacity(4096);
// The generated fixture Elm files are around 3007 bytes which includes the import of the
// target module and function
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);

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),
}