add support for custom theming in mers errors

this also includes support for the NoTheme,
a theme which doesn't add any color to mers'
output.
If you compile mers with --no-default-features,
the `colored` dependency will disappear and
mers_lib will fall back to NoTheme.
This commit is contained in:
Mark
2024-06-26 01:02:19 +02:00
parent f055d2089f
commit a78367f27c
24 changed files with 517 additions and 336 deletions

View File

@@ -44,7 +44,7 @@ impl Config {
return Err(format!("Function returns a value of type {out}, which isn't included in the type of the reference, {arg}.").into());
}
},
Err(e) => return Err(CheckError::new().msg(format!("Invalid argument type {arg} for function")).err(e)),
Err(e) => return Err(CheckError::new().msg_str(format!("Invalid argument type {arg} for function")).err(e)),
}
} else {
return Err(format!("Arguments must be (reference, function)").into());

View File

@@ -38,7 +38,7 @@ impl Config {
if let Some(f) = t.as_any().downcast_ref::<data::function::FunctionT>() {
match f.o(&Type::empty_tuple()) {
Ok(t) => out.add_all(&t),
Err(e) => return Err(CheckError::new().msg(format!("Can't call thread on a function which can't be called on an empty tuple: ")).err(e))
Err(e) => return Err(CheckError::new().msg_str(format!("Can't call thread on a function which can't be called on an empty tuple: ")).err(e))
}
} else {
return Err(format!("thread: argument wasn't a function").into());
@@ -69,7 +69,7 @@ impl Config {
out: Arc::new(|a, _i| {
for t in a.types.iter() {
if !t.as_any().is::<ThreadT>() {
return Err(CheckError::new().msg(format!("Cannot call thread_finished on a value of type {t}, which isn't a thread but part of the argument {a}.")));
return Err(CheckError::new().msg_str(format!("Cannot call thread_finished on a value of type {t}, which isn't a thread but part of the argument {a}.")));
}
}
Ok(Type::new(data::bool::BoolT))
@@ -93,7 +93,7 @@ impl Config {
if let Some(t) = t.as_any().downcast_ref::<ThreadT>() {
out.add_all(&t.0);
} else {
return Err(CheckError::new().msg(format!("Cannot call thread_await on a value of type {t}, which isn't a thread but part of the argument {a}.")));
return Err(CheckError::new().msg_str(format!("Cannot call thread_await on a value of type {t}, which isn't a thread but part of the argument {a}.")));
}
}
Ok(out)

View File

@@ -3,11 +3,9 @@ use std::{
sync::{Arc, Mutex},
};
use colored::Colorize;
use crate::{
data::{self, Data, Type},
errors::error_colors,
errors::{CheckError, EColor},
program::{self, run::CheckInfo},
};
@@ -33,11 +31,10 @@ impl Config {
Arc::new(data::tuple::TupleT(vec![])),
]))
} else {
Err(format!(
"expected (), got {}",
a.to_string().color(error_colors::FunctionArgument)
)
.into())
Err(CheckError::new().msg(vec![
("expected (), got ".to_owned(), None),
(a.to_string(), Some(EColor::FunctionArgument)),
]))
}
}),
run: Arc::new(|_a, _i| {

View File

@@ -1,10 +1,8 @@
use std::sync::{Arc, Mutex};
use colored::Colorize;
use crate::{
data::{self, Data},
errors::{error_colors, CheckError, SourceRange},
errors::{CheckError, EColor, SourceRange},
info::{self, Local},
parsing::Source,
program::{self},
@@ -36,13 +34,13 @@ impl MersStatement for IncludeMers {
return Err(CheckError::new()
.src(vec![(
self.pos_in_src.clone(),
Some(error_colors::HashIncludeErrorInIncludedFile),
Some(EColor::HashIncludeErrorInIncludedFile),
)])
.msg(
.msg(vec![(
"Error in #include! (note: inner errors may refer to a different file)"
.color(error_colors::HashIncludeErrorInIncludedFile)
.to_string(),
)
.to_owned(),
Some(EColor::HashIncludeErrorInIncludedFile),
)])
.err_with_diff_src(e))
}
};

View File

@@ -1,5 +1,5 @@
use crate::{
errors::{error_colors, CheckError, SourceRange},
errors::{CheckError, EColor, SourceRange},
info::Local,
program::{self},
};
@@ -50,9 +50,9 @@ impl MersStatement for Variable {
return Err(CheckError::new()
.src(vec![(
self.pos_in_src.clone(),
Some(error_colors::UnknownVariable),
Some(EColor::UnknownVariable),
)])
.msg(format!("No variable named '{}' found!", self.var)));
.msg_str(format!("No variable named '{}' found!", self.var)));
},
}))
}

View File

@@ -1,8 +1,6 @@
use colored::Colorize;
use crate::{
data::{Data, Type},
errors::{error_colors, CheckError, SourceRange},
errors::{CheckError, EColor, SourceRange},
parsing::types::ParsedType,
};
@@ -33,7 +31,7 @@ impl MersStatement for AsType {
CheckError::new()
.src(vec![(
self.type_pos_in_src.clone(),
Some(error_colors::BadTypeFromParsed),
Some(EColor::BadTypeFromParsed),
)])
.err(e)
})?;
@@ -43,22 +41,23 @@ impl MersStatement for AsType {
(self.pos_in_src.clone(), None),
(
self.type_pos_in_src.clone(),
Some(error_colors::AsTypeTypeAnnotation),
Some(EColor::AsTypeTypeAnnotation),
),
(
self.statement.source_range(),
Some(error_colors::AsTypeStatementWithTooBroadType),
Some(EColor::AsTypeStatementWithTooBroadType),
),
])
.msg(format!(
"Type must be included in {}, but the actual type {} isn't.",
as_type
.to_string()
.color(error_colors::AsTypeTypeAnnotation),
return_type
.to_string()
.color(error_colors::AsTypeStatementWithTooBroadType)
)));
.msg(vec![
("Type must be included in ".to_owned(), None),
(as_type.to_string(), Some(EColor::AsTypeTypeAnnotation)),
(", but the actual type ".to_owned(), None),
(
return_type.to_string(),
Some(EColor::AsTypeStatementWithTooBroadType),
),
(" isn't.".to_owned(), None),
]));
}
Ok(if self.expand_type {
as_type.clone()

View File

@@ -1,8 +1,6 @@
use colored::Colorize;
use crate::{
data::{self, Data, Type},
errors::{error_colors, CheckError, SourceRange},
errors::{CheckError, EColor, SourceRange},
};
use super::{CheckInfo, MersStatement};
@@ -37,10 +35,10 @@ impl MersStatement for AssignTo {
return Err(CheckError::new()
.src(vec![
(self.pos_in_src.clone(), None),
(self.target.source_range(), Some(error_colors::InitTo)),
(self.source.source_range(), Some(error_colors::InitFrom)),
(self.target.source_range(), Some(EColor::InitTo)),
(self.source.source_range(), Some(EColor::InitFrom)),
])
.msg(format!("Cannot initialize:"))
.msg_str(format!("Cannot initialize:"))
.err(e));
}
};
@@ -50,15 +48,17 @@ impl MersStatement for AssignTo {
return Err(CheckError::new()
.src(vec![
(self.pos_in_src.clone(), None),
(self.target.source_range(), Some(error_colors::AssignTo)),
(self.source.source_range(), Some(error_colors::AssignFrom)),
(self.target.source_range(), Some(EColor::AssignTo)),
(self.source.source_range(), Some(EColor::AssignFrom)),
])
.msg(format!(
"can't assign {} to {} because it isn't included in {}",
source.to_string().color(error_colors::AssignFrom),
target.to_string().color(error_colors::AssignTo),
t
)));
.msg(vec![
("can't assign ".to_owned(), None),
(source.to_string(), Some(EColor::AssignFrom)),
(" to ".to_owned(), None),
(target.to_string(), Some(EColor::AssignTo)),
(" because it isn't included in ".to_owned(), None),
(t.to_string(), None),
]));
}
} else {
return Err(CheckError::new()
@@ -66,13 +66,16 @@ impl MersStatement for AssignTo {
(self.pos_in_src.clone(), None),
(
self.target.source_range(),
Some(error_colors::AssignTargetNonReference),
Some(EColor::AssignTargetNonReference),
),
])
.msg(format!(
"can't assign to {}",
"non-reference!".color(error_colors::AssignTargetNonReference)
)));
.msg(vec![
("can't assign to ".to_owned(), None),
(
"non-reference!".to_owned(),
Some(EColor::AssignTargetNonReference),
),
]));
}
}
Ok(Type::empty_tuple())

View File

@@ -1,8 +1,6 @@
use colored::Colorize;
use crate::{
data::{Data, Type},
errors::{error_colors, CheckError, SourceRange},
errors::{CheckError, EColor, SourceRange},
parsing::Source,
};
@@ -44,29 +42,27 @@ impl MersStatement for Chain {
CheckError::new()
.src(vec![(
self.pos_in_src.clone(),
Some(error_colors::HashIncludeErrorInIncludedFile),
Some(EColor::HashIncludeErrorInIncludedFile),
)])
.msg(vec![(
"Error in #include:".to_owned(),
Some(EColor::HashIncludeErrorInIncludedFile),
)])
.msg(
"Error in #include:"
.color(error_colors::HashIncludeErrorInIncludedFile)
.to_string(),
)
.err_with_diff_src(e)
} else {
CheckError::new()
.src(vec![
(self.pos_in_src.clone(), None),
(
self.first.source_range(),
Some(error_colors::FunctionArgument),
),
(self.chained.source_range(), Some(error_colors::Function)),
(self.first.source_range(), Some(EColor::FunctionArgument)),
(self.chained.source_range(), Some(EColor::Function)),
])
.msg(vec![
("Can't call ".to_owned(), None),
("this function".to_owned(), Some(EColor::Function)),
(" with an argument of type ".to_owned(), None),
(arg.to_string(), Some(EColor::FunctionArgument)),
(":".to_owned(), None),
])
.msg(format!(
"Can't call {} with an argument of type {}:",
"this function".color(error_colors::Function),
arg.to_string().color(error_colors::FunctionArgument)
))
.err(e)
})
}
@@ -77,13 +73,14 @@ impl MersStatement for Chain {
(self.pos_in_src.clone(), None),
(
self.chained.source_range(),
Some(error_colors::ChainWithNonFunction),
Some(EColor::ChainWithNonFunction),
),
])
.msg(format!(
"cannot chain with a non-function ({})",
func.to_string().color(error_colors::ChainWithNonFunction)
)));
.msg(vec![
("cannot chain with a non-function (".to_owned(), None),
(func.to_string(), Some(EColor::ChainWithNonFunction)),
(")".to_owned(), None),
]));
}
}
Ok(o)
@@ -98,12 +95,12 @@ impl MersStatement for Chain {
Err(e) => Err(if let Some(_) = &self.as_part_of_include {
CheckError::new().err_with_diff_src(e).src(vec![(
self.pos_in_src.clone(),
Some(error_colors::StacktraceDescendHashInclude),
Some(EColor::StacktraceDescendHashInclude),
)])
} else {
CheckError::new().err(e).src(vec![
(self.pos_in_src.clone(), None),
(self.source_range(), Some(error_colors::StacktraceDescend)),
(self.source_range(), Some(EColor::StacktraceDescend)),
])
}),
}

View File

@@ -1,10 +1,8 @@
use std::{fmt::Debug, sync::Arc};
use colored::Colorize;
use crate::{
data::{Data, Type},
errors::{CheckError, SourceRange},
errors::{CheckError, EColor, SourceRange},
};
use super::{CheckInfo, Info, MersStatement};
@@ -45,12 +43,17 @@ impl MersStatement for CustomType {
} else {
if let Err(e) = t {
return Err(CheckError::new()
.msg(format!(
" {} {} {} (`[[_] := ...]` indicates that `...` must be type-correct)",
"<<".bright_red(),
"Custom type-test failed!".bright_red(),
">>".bright_red(),
))
.msg(vec![
(
" << Custom type-test failed! >>".to_owned(),
Some(EColor::CustomTypeTestFailed),
),
(
" (`[[_] := ...]` indicates that `...` must be type-correct)"
.to_owned(),
None,
),
])
.err(e));
}
}

View File

@@ -1,10 +1,8 @@
use std::sync::Arc;
use colored::Colorize;
use crate::{
data::{self, tuple::TupleT, Data, Type},
errors::{error_colors, CheckError, SourceRange},
errors::{CheckError, EColor, SourceRange},
};
use super::MersStatement;
@@ -33,16 +31,21 @@ impl MersStatement for If {
(self.pos_in_src.clone(), None),
(
self.condition.source_range(),
Some(error_colors::IfConditionNotBool),
Some(EColor::IfConditionNotBool),
),
])
.msg(format!(
"The {} in an if-statement must return bool, not {}",
"condition".color(error_colors::IfConditionNotBool),
cond_return_type
.to_string()
.color(error_colors::IfConditionNotBool),
)));
.msg(vec![
("The ".to_owned(), None),
("condition".to_owned(), Some(EColor::IfConditionNotBool)),
(
" in an if-statement must return bool, not ".to_owned(),
None,
),
(
cond_return_type.to_string(),
Some(EColor::IfConditionNotBool),
),
]));
}
let mut t = self.on_true.check(info, None)?;
if let Some(f) = &self.on_false {

View File

@@ -7,7 +7,7 @@ use std::{
use crate::{
data::{self, Data, Type},
errors::{error_colors, CheckError, SourceRange},
errors::{CheckError, EColor, SourceRange},
info,
};
@@ -98,10 +98,10 @@ pub trait MersStatement: Debug + Send + Sync {
if let Some(cutoff) = info.global.limit_runtime {
if Instant::now() >= cutoff {
return Err(CheckError::new()
.msg("maximum runtime exceeded".to_owned())
.msg_str("maximum runtime exceeded".to_owned())
.src(vec![(
self.source_range(),
Some(error_colors::MaximumRuntimeExceeded),
Some(EColor::MaximumRuntimeExceeded),
)]));
}
}

View File

@@ -1,10 +1,8 @@
use std::collections::VecDeque;
use colored::Colorize;
use crate::{
data::{self, object::ObjectT, Data, Type},
errors::{error_colors, CheckError, SourceRange},
errors::{CheckError, EColor, SourceRange},
};
use super::MersStatement;
@@ -31,34 +29,47 @@ impl MersStatement for Object {
for (i, ((sn, _), (tn, t))) in self.elems.iter().zip(t.0.iter()).enumerate()
{
if sn != tn {
return Err(format!("can't init an {} with type {}{} - field mismatch: {sn} != {tn}", "object".color(error_colors::InitTo),
t.to_string().color(error_colors::InitFrom),
if print_is_part_of {
format!(
", which is part of {}",
init_to.to_string().color(error_colors::InitFrom)
)
} else {
format!("")
}
).into());
return Err(CheckError::new().msg(vec![
("can't init an ".to_owned(), None),
("object".to_owned(), Some(EColor::InitTo)),
(" with type ".to_owned(), None),
(t.to_string(), Some(EColor::InitFrom)),
if print_is_part_of {
(", which is part of ".to_owned(), None)
} else {
(String::new(), None)
},
if print_is_part_of {
(init_to.to_string(), Some(EColor::InitFrom))
} else {
(String::new(), None)
},
(" - field mismatch: ".to_owned(), None),
(sn.to_owned(), None),
(" != ".to_owned(), None),
(tn.to_owned(), None),
]));
}
acc[i].add_all(&t);
}
} else {
return Err(format!(
"can't init an {} with type {}{} - source has {}",
"object".color(error_colors::InitTo),
t.to_string().color(error_colors::InitFrom),
return Err(CheckError::new().msg(vec![
("can't init an ".to_owned(), None),
("object".to_owned(), Some(EColor::InitTo)),
(" with type ".to_owned(), None),
(t.to_string(), Some(EColor::InitFrom)),
if print_is_part_of {
format!(
", which is part of {}",
init_to.to_string().color(error_colors::InitFrom)
)
(", which is part of ".to_owned(), None)
} else {
format!("")
(format!(""), None)
},
if self.elems.len() > t.0.len() {
if print_is_part_of {
(init_to.to_string(), Some(EColor::InitFrom))
} else {
(format!(""), None)
},
(" - source has ".to_owned(), None),
(if self.elems.len() > t.0.len() {
format!("less fields ({}, not {})", t.0.len(), self.elems.len())
} else {
format!(
@@ -74,25 +85,30 @@ impl MersStatement for Object {
.collect::<String>(),
data::object::ObjectT(t.0.iter().take(self.elems.len()).cloned().collect())
)
}
)
.into());
}, None)
]));
}
} else {
return Err(format!(
"can't init an {} with type {}{} - only objects can be assigned to objects",
"object".color(error_colors::InitTo),
t.to_string().color(error_colors::InitFrom),
return Err(CheckError::new().msg(vec![
("can't init an ".to_owned(), None),
("object".to_owned(), Some(EColor::InitTo)),
(" with type ".to_owned(), None),
(t.to_string(), Some(EColor::InitFrom)),
if print_is_part_of {
format!(
", which is part of {}",
init_to.to_string().color(error_colors::InitFrom)
)
(", which is part of ".to_owned(), None)
} else {
format!("")
}
)
.into());
(format!(""), None)
},
if print_is_part_of {
(init_to.to_string(), Some(EColor::InitFrom))
} else {
(format!(""), None)
},
(
" - only objects can be assigned to objects".to_owned(),
None,
),
]));
}
}
Some(acc)

View File

@@ -2,7 +2,7 @@ use std::sync::{Arc, Mutex};
use crate::{
data::{self, Data, Type},
errors::{error_colors, CheckError, SourceRange},
errors::{CheckError, EColor, SourceRange},
};
use super::{Info, MersStatement};
@@ -62,16 +62,13 @@ impl MersStatement for Try {
}
} else {
return Err(CheckError::new()
.msg(format!(
.msg_str(format!(
"try: #{} is not a function, type is {ft} within {func}.",
i + 1
))
.src(vec![
(self.source_range(), None),
(
self.funcs[i].source_range(),
Some(error_colors::TryNotAFunction),
),
(self.funcs[i].source_range(), Some(EColor::TryNotAFunction)),
]));
}
}
@@ -88,15 +85,17 @@ impl MersStatement for Try {
}
if !found {
let mut err = CheckError::new()
.msg(format!(
.msg_str(format!(
"try: no function found for argument of type {arg}."
))
.src(vec![(
self.pos_in_src.clone(),
Some(error_colors::TryNoFunctionFound),
Some(EColor::TryNoFunctionFound),
)]);
for (i, e) in errs.into_iter().enumerate() {
err = err.msg(format!("Error for function #{}:", i + 1)).err(e);
err = err
.msg_str(format!("Error for function #{}:", i + 1))
.err(e);
}
return Err(err);
}

View File

@@ -1,10 +1,8 @@
use std::collections::VecDeque;
use colored::Colorize;
use crate::{
data::{self, tuple::TupleT, Data, Type},
errors::{error_colors, CheckError, SourceRange},
errors::{CheckError, EColor, SourceRange},
};
use super::MersStatement;
@@ -32,33 +30,54 @@ impl MersStatement for Tuple {
vec[i].add_all(&e);
}
} else {
return Err(
format!("can't init a {} with type {}{} - only tuples with the same length ({}) can be assigned.",
"tuple".color(error_colors::InitTo),
t.to_string().color(error_colors::InitFrom),
return Err(CheckError::new().msg(vec![
("can't init a ".to_owned(), None),
("tuple".to_owned(), Some(EColor::InitTo)),
(" with type ".to_owned(), None),
(t.to_string(), Some(EColor::InitFrom)),
(
if print_is_part_of {
format!(", which is part of {}", init_to.to_string().color(error_colors::InitFrom))
", which is part of ".to_owned()
} else {
format!("")
String::new()
},
self.elems.len()
).into());
None,
),
if print_is_part_of {
(init_to.to_string(), Some(EColor::InitFrom))
} else {
(String::new(), None)
},
(
format!(
" - only tuples with the same length ({}) can be assigned",
self.elems.len()
),
None,
),
]));
}
} else {
return Err(format!(
"can't init a {} with type {}{} - only tuples can be assigned to tuples",
"tuple".color(error_colors::InitTo),
t.to_string().color(error_colors::InitFrom),
return Err(CheckError::new().msg(vec![
("can't init a ".to_owned(), None),
("tuple".to_owned(), Some(EColor::InitTo)),
(" with type ".to_owned(), None),
(t.to_string(), Some(EColor::InitFrom)),
(
if print_is_part_of {
", which is part of ".to_owned()
} else {
String::new()
},
None,
),
if print_is_part_of {
format!(
", which is part of {}",
init_to.to_string().color(error_colors::InitFrom)
)
(init_to.to_string(), Some(EColor::InitFrom))
} else {
format!("")
}
)
.into());
(String::new(), None)
},
(" - only tuples can be assigned to tuples".to_owned(), None),
]));
}
}
Some(vec)