summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--Cargo.toml1
-rw-r--r--src/library/mod.rs1
-rw-r--r--src/library/utility/data.rs43
-rw-r--r--tests/ref/utility/data.png (renamed from tests/ref/utility/csv.png)bin8603 -> 8603 bytes
-rw-r--r--tests/res/bad.json4
-rw-r--r--tests/res/zoo.json20
-rw-r--r--tests/typ/utility/data.typ (renamed from tests/typ/utility/csv.typ)16
8 files changed, 85 insertions, 1 deletions
diff --git a/Cargo.lock b/Cargo.lock
index ccaf9391..e4a8add7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1061,6 +1061,7 @@ dependencies = [
"rustybuzz",
"same-file",
"serde",
+ "serde_json",
"siphasher",
"subsetter",
"svg2pdf",
diff --git a/Cargo.toml b/Cargo.toml
index 65afa214..9e3137b1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -41,6 +41,7 @@ syntect = { version = "5", default-features = false, features = ["default-syntax
rex = { git = "https://github.com/laurmaedje/ReX" }
lipsum = { git = "https://github.com/reknih/lipsum" }
csv = "1"
+serde_json = "1"
# PDF export
miniz_oxide = "0.5"
diff --git a/src/library/mod.rs b/src/library/mod.rs
index 566a4f26..d806f298 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -97,6 +97,7 @@ pub fn new() -> Scope {
std.def_fn("symbol", utility::symbol);
std.def_fn("lorem", utility::lorem);
std.def_fn("csv", utility::csv);
+ std.def_fn("json", utility::json);
// Predefined colors.
std.define("black", Color::BLACK);
diff --git a/src/library/utility/data.rs b/src/library/utility/data.rs
index 1ae8949a..e3efe6e7 100644
--- a/src/library/utility/data.rs
+++ b/src/library/utility/data.rs
@@ -41,3 +41,46 @@ fn format_csv_error(error: csv::Error) -> String {
_ => "failed to parse csv file".into(),
}
}
+
+/// Read structured data from a JSON file.
+pub fn json(vm: &mut Vm, args: &mut Args) -> SourceResult<Value> {
+ let Spanned { v: path, span } =
+ args.expect::<Spanned<EcoString>>("path to json file")?;
+
+ let path = vm.locate(&path).at(span)?;
+ let data = vm.world.file(&path).at(span)?;
+ let value: serde_json::Value =
+ serde_json::from_slice(&data).map_err(format_json_error).at(span)?;
+
+ Ok(convert_json(value))
+}
+
+/// Convert a JSON value to a Typst value.
+fn convert_json(value: serde_json::Value) -> Value {
+ match value {
+ serde_json::Value::Null => Value::None,
+ serde_json::Value::Bool(v) => Value::Bool(v),
+ serde_json::Value::Number(v) => match v.as_i64() {
+ Some(int) => Value::Int(int),
+ None => Value::Float(v.as_f64().unwrap_or(f64::NAN)),
+ },
+ serde_json::Value::String(v) => Value::Str(v.into()),
+ serde_json::Value::Array(v) => {
+ Value::Array(v.into_iter().map(convert_json).collect())
+ }
+ serde_json::Value::Object(v) => Value::Dict(
+ v.into_iter()
+ .map(|(key, value)| (key.into(), convert_json(value)))
+ .collect(),
+ ),
+ }
+}
+
+/// Format the user-facing JSON error message.
+fn format_json_error(error: serde_json::Error) -> String {
+ assert!(error.is_syntax() || error.is_eof());
+ format!(
+ "failed to parse json file: syntax error in line {}",
+ error.line()
+ )
+}
diff --git a/tests/ref/utility/csv.png b/tests/ref/utility/data.png
index 69e0ae38..69e0ae38 100644
--- a/tests/ref/utility/csv.png
+++ b/tests/ref/utility/data.png
Binary files differ
diff --git a/tests/res/bad.json b/tests/res/bad.json
new file mode 100644
index 00000000..cd5d0366
--- /dev/null
+++ b/tests/res/bad.json
@@ -0,0 +1,4 @@
+{
+ "valid": true,
+ "invalid": True
+}
diff --git a/tests/res/zoo.json b/tests/res/zoo.json
new file mode 100644
index 00000000..3b5a7ffc
--- /dev/null
+++ b/tests/res/zoo.json
@@ -0,0 +1,20 @@
+[
+ {
+ "name": "Debby",
+ "species": "Rhinoceros",
+ "weight": 1900,
+ "length": 390
+ },
+ {
+ "name": "Fluffy",
+ "species": "Tiger",
+ "weight": 115,
+ "length": 310
+ },
+ {
+ "name": "Sleepy",
+ "species": "Dolphin",
+ "weight": 150,
+ "length": 180
+ }
+]
diff --git a/tests/typ/utility/csv.typ b/tests/typ/utility/data.typ
index 146cafae..4fdb84c2 100644
--- a/tests/typ/utility/csv.typ
+++ b/tests/typ/utility/data.typ
@@ -1,6 +1,9 @@
-// Test reading structured CSV data.
+// Test reading structured data.
+// Ref: false
---
+// Test reading CSV data.
+// Ref: true
#set page(width: auto)
#let data = csv("/res/zoo.csv")
#let cells = data(0).map(strong) + data.slice(1).flatten()
@@ -13,3 +16,14 @@
---
// Error: 6-20 failed to parse csv file: found 3 instead of 2 fields in line 3
#csv("/res/bad.csv")
+
+---
+// Test reading JSON data.
+#let data = json("/res/zoo.json")
+#test(data.len(), 3)
+#test(data(0).name, "Debby")
+#test(data(2).weight, 150)
+
+---
+// Error: 7-22 failed to parse json file: syntax error in line 3
+#json("/res/bad.json")