summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarmoGlace <23212967+HarmoGlace@users.noreply.github.com>2023-04-26 11:31:32 +0200
committerGitHub <noreply@github.com>2023-04-26 11:31:32 +0200
commit6134e3f4ee5298153c36d344df97f36279931c33 (patch)
treec4787313543521f079a23e3b6aeb7a4d2a5d0945
parent7cb63d1aae349541aabc165d8c9e5c0943e38511 (diff)
Add toml support (#807)
-rw-r--r--Cargo.lock53
-rw-r--r--assets/files/bad.toml1
-rw-r--r--assets/files/informations.toml11
-rw-r--r--assets/files/toml_types.toml11
-rw-r--r--library/Cargo.toml1
-rw-r--r--library/src/compute/data.rs81
-rw-r--r--library/src/lib.rs1
-rw-r--r--tests/typ/compute/data.typ17
8 files changed, 176 insertions, 0 deletions
diff --git a/Cargo.lock b/Cargo.lock
index efe42339..e702406c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1849,6 +1849,15 @@ dependencies = [
]
[[package]]
+name = "serde_spanned"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4"
+dependencies = [
+ "serde",
+]
+
+[[package]]
name = "serde_yaml"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2140,6 +2149,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
+name = "toml"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.19.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
+]
+
+[[package]]
name = "tracing"
version = "0.1.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2348,6 +2391,7 @@ dependencies = [
"serde_yaml",
"smallvec",
"syntect",
+ "toml",
"tracing",
"ttf-parser 0.18.1",
"typed-arena",
@@ -2883,6 +2927,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
+name = "winnow"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
name = "wyz"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/assets/files/bad.toml b/assets/files/bad.toml
new file mode 100644
index 00000000..2d5da4d3
--- /dev/null
+++ b/assets/files/bad.toml
@@ -0,0 +1 @@
+"only a string" \ No newline at end of file
diff --git a/assets/files/informations.toml b/assets/files/informations.toml
new file mode 100644
index 00000000..f6e3b5fb
--- /dev/null
+++ b/assets/files/informations.toml
@@ -0,0 +1,11 @@
+authors = ["Mr Robert", "Miss Enola", "Mr Jonathan"]
+version = "1.0.3"
+
+series = [
+ { name = "attack on titan", fans = 500},
+ { name = "demon slayer", fans = 10}
+]
+
+[informations]
+location = "room A204"
+pages = 47 \ No newline at end of file
diff --git a/assets/files/toml_types.toml b/assets/files/toml_types.toml
new file mode 100644
index 00000000..08f1118d
--- /dev/null
+++ b/assets/files/toml_types.toml
@@ -0,0 +1,11 @@
+string = "wonderful"
+integer = 42
+float = 3.14
+boolean = true
+date_time = 2023-02-01T15:38:57Z
+array = [1, "string", 3.0, false]
+inline_table = { first = "amazing", second = "greater" }
+
+[table]
+element = 5
+others = [false, "indeed", 7] \ No newline at end of file
diff --git a/library/Cargo.toml b/library/Cargo.toml
index 889ea70d..c0c20c41 100644
--- a/library/Cargo.toml
+++ b/library/Cargo.toml
@@ -40,3 +40,4 @@ unicode-segmentation = "1"
xi-unicode = "0.3"
chinese-number = { version = "0.7.2", default-features = false, features = ["number-to-chinese"] }
tracing = "0.1.37"
+toml = { version = "0.7.3", default-features = false, features = ["parse"]}
diff --git a/library/src/compute/data.rs b/library/src/compute/data.rs
index fc81435c..02586ddc 100644
--- a/library/src/compute/data.rs
+++ b/library/src/compute/data.rs
@@ -207,6 +207,87 @@ fn format_json_error(error: serde_json::Error) -> EcoString {
eco_format!("failed to parse json file: syntax error in line {}", error.line())
}
+/// Read structured data from a TOML file.
+///
+/// The file must contain a valid TOML table. Tables will be
+/// converted into Typst dictionaries, and TOML arrays will be converted into
+/// Typst arrays. Strings and booleans will be converted into the Typst
+/// equivalents, numbers will be converted to floats or integers depending on
+/// whether they are whole numbers. TOML DateTim will be converted to strings.
+///
+/// The function returns a dictionary.
+///
+/// The JSON files in the example contain objects with the keys `temperature`,
+/// `unit`, and `weather`.
+///
+/// ## Example
+/// ```example
+/// #let informations(content) = {
+/// [This work is made by #content.authors.join(", ", last: " and "). We are currently at version #content.version.
+/// The favorites series of the audience are ]
+/// for serie in content.series [
+/// - #serie.name with #serie.fans fans.
+/// ]
+/// [We need to submit our work in #content.informations.location, we currently have #content.informations.pages pages.]
+/// }
+///
+///
+/// #informations(toml("informations.toml"))
+/// ```
+///
+/// Display: TOML
+/// Category: data-loading
+/// Returns: dictionary
+#[func]
+pub fn toml(
+ /// Path to a TOML file.
+ path: Spanned<EcoString>,
+) -> Value {
+ let Spanned { v: path, span } = path;
+ let path = vm.locate(&path).at(span)?;
+ let data = vm.world().file(&path).at(span)?;
+
+ let raw = std::str::from_utf8(&data)
+ .map_err(|_| "file is not valid utf-8")
+ .at(span)?;
+
+ let value: toml::Value = toml::from_str(raw).map_err(format_toml_error).at(span)?;
+ convert_toml(value)
+}
+
+/// Convert a TOML value to a Typst value.
+fn convert_toml(value: toml::Value) -> Value {
+ match value {
+ toml::Value::String(v) => Value::Str(v.into()),
+ toml::Value::Integer(v) => Value::Int(v),
+ toml::Value::Float(v) => Value::Float(v),
+ toml::Value::Boolean(v) => Value::Bool(v),
+ toml::Value::Array(v) => Value::Array(v.into_iter().map(convert_toml).collect()),
+ toml::Value::Table(v) => Value::Dict(
+ v.into_iter()
+ .map(|(key, value)| (key.into(), convert_toml(value)))
+ .collect(),
+ ),
+ // Todo: make it use native date/time object(s) once it is implemented.
+ toml::Value::Datetime(v) => Value::Str(v.to_string().into()),
+ }
+}
+
+/// Format the user-facing TOML error message.
+#[track_caller]
+fn format_toml_error(error: toml::de::Error) -> String {
+ if let Some(range) = error.span() {
+ format!(
+ "failed to parse toml file: {message}, index {start}-{end}",
+ message = error.message(),
+ start = range.start,
+ end = range.end
+ )
+ } else {
+ format!("failed to parse toml file: {message}", message = error.message())
+ }
+}
+
/// Read structured data from a YAML file.
///
/// The file must contain a valid YAML object or array. YAML mappings will be
diff --git a/library/src/lib.rs b/library/src/lib.rs
index e7a23cd7..0350746d 100644
--- a/library/src/lib.rs
+++ b/library/src/lib.rs
@@ -131,6 +131,7 @@ fn global(math: Module, calc: Module) -> Module {
global.define("read", compute::read);
global.define("csv", compute::csv);
global.define("json", compute::json);
+ global.define("toml", compute::toml);
global.define("yaml", compute::yaml);
global.define("xml", compute::xml);
diff --git a/tests/typ/compute/data.typ b/tests/typ/compute/data.typ
index cfd761df..8b50f7c4 100644
--- a/tests/typ/compute/data.typ
+++ b/tests/typ/compute/data.typ
@@ -42,6 +42,23 @@
#json("/bad.json")
---
+// Test reading TOML data.
+#let data = toml("/toml_types.toml")
+#test(data.string, "wonderful")
+#test(data.integer, 42)
+#test(data.float, 3.14)
+#test(data.boolean, true)
+#test(data.date_time, "2023-02-01T15:38:57Z")
+#test(data.array, (1, "string", 3.0, false))
+#test(data.inline_table, ("first": "amazing", "second": "greater") )
+#test(data.table.element, 5)
+#test(data.table.others, (false, "indeed", 7))
+
+---
+// Error: 7-18 failed to parse toml file: expected `.`, `=`, index 15-15
+#toml("/bad.toml")
+
+---
// Test reading YAML data
#let data = yaml("/yamltypes.yaml")
#test(data.len(), 7)