feat: start to report errors instead of panicing
This commit is contained in:
parent
f7aff7585d
commit
601137dd16
3 changed files with 95 additions and 38 deletions
|
|
@ -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
|
||||
|
|
|
|||
67
src/main.rs
67
src/main.rs
|
|
@ -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
65
src/reporting.rs
Normal 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),
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue