summaryrefslogtreecommitdiff
path: root/src/library/utility
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-09-20 19:49:47 +0200
committerLaurenz <laurmaedje@gmail.com>2022-09-20 19:49:47 +0200
commit3760748fddd3b793c79c370398a9d4a3fc5afc04 (patch)
treeb1a615e510aa231cfe9757a9c0a35a375e32e3ba /src/library/utility
parent757a701c1aa2a6fb80033c7e75666661818da6f9 (diff)
Refactor error handling
Diffstat (limited to 'src/library/utility')
-rw-r--r--src/library/utility/color.rs6
-rw-r--r--src/library/utility/data.rs47
-rw-r--r--src/library/utility/math.rs20
-rw-r--r--src/library/utility/mod.rs8
-rw-r--r--src/library/utility/string.rs16
5 files changed, 55 insertions, 42 deletions
diff --git a/src/library/utility/color.rs b/src/library/utility/color.rs
index a7d55d1c..a5a5704d 100644
--- a/src/library/utility/color.rs
+++ b/src/library/utility/color.rs
@@ -3,13 +3,13 @@ use std::str::FromStr;
use crate::library::prelude::*;
/// Create a grayscale color.
-pub fn luma(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn luma(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
let Component(luma) = args.expect("gray component")?;
Ok(Value::Color(LumaColor::new(luma).into()))
}
/// Create an RGB(A) color.
-pub fn rgb(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn rgb(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
Ok(Value::Color(
if let Some(string) = args.find::<Spanned<EcoString>>()? {
match RgbaColor::from_str(&string.v) {
@@ -27,7 +27,7 @@ pub fn rgb(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
}
/// Create a CMYK color.
-pub fn cmyk(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn cmyk(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
let RatioComponent(c) = args.expect("cyan component")?;
let RatioComponent(m) = args.expect("magenta component")?;
let RatioComponent(y) = args.expect("yellow component")?;
diff --git a/src/library/utility/data.rs b/src/library/utility/data.rs
index 59f3d351..1ae8949a 100644
--- a/src/library/utility/data.rs
+++ b/src/library/utility/data.rs
@@ -1,30 +1,43 @@
+use std::fmt::Write;
+
use crate::library::prelude::*;
/// Read structured data from a CSV file.
-pub fn csv(vm: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn csv(vm: &mut Vm, args: &mut Args) -> SourceResult<Value> {
let Spanned { v: path, span } =
args.expect::<Spanned<EcoString>>("path to csv file")?;
let path = vm.locate(&path).at(span)?;
- let try_load = || -> io::Result<Value> {
- let data = vm.world.file(&path)?;
+ let data = vm.world.file(&path).at(span)?;
- let mut builder = csv::ReaderBuilder::new();
- builder.has_headers(false);
+ let mut builder = csv::ReaderBuilder::new();
+ builder.has_headers(false);
- let mut reader = builder.from_reader(data.as_slice());
- let mut vec = vec![];
+ let mut reader = builder.from_reader(data.as_slice());
+ let mut vec = vec![];
- for result in reader.records() {
- vec.push(Value::Array(
- result?.iter().map(|field| Value::Str(field.into())).collect(),
- ))
- }
+ for result in reader.records() {
+ let row = result.map_err(format_csv_error).at(span)?;
+ let array = row.iter().map(|field| Value::Str(field.into())).collect();
+ vec.push(Value::Array(array))
+ }
- Ok(Value::Array(Array::from_vec(vec)))
- };
+ Ok(Value::Array(Array::from_vec(vec)))
+}
- try_load()
- .map_err(|err| failed_to_load("csv file", &path, err))
- .at(span)
+/// Format the user-facing CSV error message.
+fn format_csv_error(error: csv::Error) -> String {
+ match error.kind() {
+ csv::ErrorKind::Utf8 { .. } => "file is not valid utf-8".into(),
+ csv::ErrorKind::UnequalLengths { pos, expected_len, len } => {
+ let mut msg = format!(
+ "failed to parse csv file: found {len} instead of {expected_len} fields"
+ );
+ if let Some(pos) = pos {
+ write!(msg, " in line {}", pos.line()).unwrap();
+ }
+ msg
+ }
+ _ => "failed to parse csv file".into(),
+ }
}
diff --git a/src/library/utility/math.rs b/src/library/utility/math.rs
index 47648282..7c3af490 100644
--- a/src/library/utility/math.rs
+++ b/src/library/utility/math.rs
@@ -3,7 +3,7 @@ use std::cmp::Ordering;
use crate::library::prelude::*;
/// Convert a value to an integer.
-pub fn int(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn int(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
let Spanned { v, span } = args.expect("value")?;
Ok(Value::Int(match v {
Value::Bool(v) => v as i64,
@@ -18,7 +18,7 @@ pub fn int(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
}
/// Convert a value to a float.
-pub fn float(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn float(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
let Spanned { v, span } = args.expect("value")?;
Ok(Value::Float(match v {
Value::Int(v) => v as f64,
@@ -32,7 +32,7 @@ pub fn float(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
}
/// The absolute value of a numeric value.
-pub fn abs(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn abs(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
let Spanned { v, span } = args.expect("numeric value")?;
Ok(match v {
Value::Int(v) => Value::Int(v.abs()),
@@ -48,17 +48,17 @@ pub fn abs(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
}
/// The minimum of a sequence of values.
-pub fn min(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn min(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
minmax(args, Ordering::Less)
}
/// The maximum of a sequence of values.
-pub fn max(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn max(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
minmax(args, Ordering::Greater)
}
/// Find the minimum or maximum of a sequence of values.
-fn minmax(args: &mut Args, goal: Ordering) -> TypResult<Value> {
+fn minmax(args: &mut Args, goal: Ordering) -> SourceResult<Value> {
let mut extremum = args.expect::<Value>("value")?;
for Spanned { v, span } in args.all::<Spanned<Value>>()? {
match v.partial_cmp(&extremum) {
@@ -79,17 +79,17 @@ fn minmax(args: &mut Args, goal: Ordering) -> TypResult<Value> {
}
/// Whether an integer is even.
-pub fn even(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn even(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
Ok(Value::Bool(args.expect::<i64>("integer")? % 2 == 0))
}
/// Whether an integer is odd.
-pub fn odd(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn odd(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
Ok(Value::Bool(args.expect::<i64>("integer")? % 2 != 0))
}
/// The modulo of two numbers.
-pub fn mod_(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn mod_(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
let Spanned { v: v1, span: span1 } = args.expect("integer or float")?;
let Spanned { v: v2, span: span2 } = args.expect("integer or float")?;
@@ -119,7 +119,7 @@ pub fn mod_(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
}
/// Create a sequence of numbers.
-pub fn range(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn range(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
let first = args.expect::<i64>("end")?;
let (start, end) = match args.eat::<i64>()? {
Some(second) => (first, second),
diff --git a/src/library/utility/mod.rs b/src/library/utility/mod.rs
index 40a107ba..3fc413f7 100644
--- a/src/library/utility/mod.rs
+++ b/src/library/utility/mod.rs
@@ -15,12 +15,12 @@ use crate::library::prelude::*;
use crate::source::Source;
/// The name of a value's type.
-pub fn type_(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn type_(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
Ok(args.expect::<Value>("value")?.type_name().into())
}
/// Ensure that a condition is fulfilled.
-pub fn assert(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn assert(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
let Spanned { v, span } = args.expect::<Spanned<bool>>("condition")?;
if !v {
bail!(span, "assertion failed");
@@ -29,7 +29,7 @@ pub fn assert(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
}
/// Evaluate a string as Typst markup.
-pub fn eval(vm: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn eval(vm: &mut Vm, args: &mut Args) -> SourceResult<Value> {
let Spanned { v: text, span } = args.expect::<Spanned<String>>("source")?;
// Parse the source and set a synthetic span for all nodes.
@@ -44,7 +44,7 @@ pub fn eval(vm: &mut Vm, args: &mut Args) -> TypResult<Value> {
// Handle control flow.
if let Some(flow) = sub.flow {
- return Err(flow.forbidden());
+ bail!(flow.forbidden());
}
Ok(Value::Content(result?))
diff --git a/src/library/utility/string.rs b/src/library/utility/string.rs
index d825d84b..91a990a9 100644
--- a/src/library/utility/string.rs
+++ b/src/library/utility/string.rs
@@ -2,12 +2,12 @@ use crate::eval::Regex;
use crate::library::prelude::*;
/// The string representation of a value.
-pub fn repr(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn repr(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
Ok(args.expect::<Value>("value")?.repr().into())
}
/// Convert a value to a string.
-pub fn str(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn str(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
let Spanned { v, span } = args.expect("value")?;
Ok(Value::Str(match v {
Value::Int(v) => format_str!("{}", v),
@@ -18,33 +18,33 @@ pub fn str(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
}
/// Create blind text.
-pub fn lorem(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn lorem(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
let words: usize = args.expect("number of words")?;
Ok(Value::Str(lipsum::lipsum(words).into()))
}
/// Create a regular expression.
-pub fn regex(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn regex(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
let Spanned { v, span } = args.expect::<Spanned<EcoString>>("regular expression")?;
Ok(Regex::new(&v).at(span)?.into())
}
/// Converts an integer into one or multiple letters.
-pub fn letter(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn letter(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
numbered(Numbering::Letter, args)
}
/// Converts an integer into a roman numeral.
-pub fn roman(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn roman(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
numbered(Numbering::Roman, args)
}
/// Convert a number into a symbol.
-pub fn symbol(_: &mut Vm, args: &mut Args) -> TypResult<Value> {
+pub fn symbol(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
numbered(Numbering::Symbol, args)
}
-fn numbered(numbering: Numbering, args: &mut Args) -> TypResult<Value> {
+fn numbered(numbering: Numbering, args: &mut Args) -> SourceResult<Value> {
let n = args.expect::<usize>("non-negative integer")?;
Ok(Value::Str(numbering.apply(n).into()))
}