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"
|
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
|
||||||
|
|
|
||||||
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 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
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