summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <ritters_werth@outlook.com>2023-12-18 12:18:41 +0100
committerGitHub <noreply@github.com>2023-12-18 12:18:41 +0100
commit22ba6825db3b82e0b0f83ef6052f17289893e385 (patch)
treecf69e7ac0eeb49f13ffb0b81e36461e07cb766ba
parent356bdeba18153efd4209657182971e22bdaf4db2 (diff)
Key/Value data from CLI (#2894)
-rw-r--r--crates/typst-cli/src/args.rs26
-rw-r--r--crates/typst-cli/src/world.rs17
-rw-r--r--crates/typst-docs/src/lib.rs2
-rw-r--r--crates/typst/src/foundations/mod.rs4
-rw-r--r--crates/typst/src/foundations/sys.rs5
-rw-r--r--crates/typst/src/lib.rs41
-rw-r--r--docs/reference/groups.yml16
-rw-r--r--tests/fuzz/src/compile.rs2
-rw-r--r--tests/src/benches.rs2
-rw-r--r--tests/src/tests.rs2
10 files changed, 94 insertions, 23 deletions
diff --git a/crates/typst-cli/src/args.rs b/crates/typst-cli/src/args.rs
index 075412cd..8f0261f1 100644
--- a/crates/typst-cli/src/args.rs
+++ b/crates/typst-cli/src/args.rs
@@ -1,6 +1,7 @@
use std::fmt::{self, Display, Formatter};
use std::path::PathBuf;
+use clap::builder::ValueParser;
use clap::{ArgAction, Args, Parser, Subcommand, ValueEnum};
use semver::Version;
@@ -116,6 +117,15 @@ pub struct SharedArgs {
#[clap(long = "root", env = "TYPST_ROOT", value_name = "DIR")]
pub root: Option<PathBuf>,
+ /// Add a string key-value pair visible through `sys.inputs`
+ #[clap(
+ long = "input",
+ value_name = "key=value",
+ action = ArgAction::Append,
+ value_parser = ValueParser::new(parse_input_pair),
+ )]
+ pub inputs: Vec<(String, String)>,
+
/// Adds additional directories to search for fonts
#[clap(
long = "font-path",
@@ -134,6 +144,22 @@ pub struct SharedArgs {
pub diagnostic_format: DiagnosticFormat,
}
+/// Parses key/value pairs split by the first equal sign.
+///
+/// This function will return an error if the argument contains no equals sign
+/// or contains the key (before the equals sign) is empty.
+fn parse_input_pair(raw: &str) -> Result<(String, String), String> {
+ let (key, val) = raw
+ .split_once('=')
+ .ok_or("input must be a key and a value separated by an equal sign")?;
+ let key = key.trim().to_owned();
+ if key.is_empty() {
+ return Err("the key was missing or empty".to_owned());
+ }
+ let val = val.trim().to_owned();
+ Ok((key, val))
+}
+
/// Lists all discovered fonts in system and custom font paths
#[derive(Debug, Clone, Parser)]
pub struct FontsCommand {
diff --git a/crates/typst-cli/src/world.rs b/crates/typst-cli/src/world.rs
index 8f8a7e5c..cd4244fc 100644
--- a/crates/typst-cli/src/world.rs
+++ b/crates/typst-cli/src/world.rs
@@ -7,7 +7,7 @@ use chrono::{DateTime, Datelike, Local};
use comemo::Prehashed;
use ecow::eco_format;
use typst::diag::{FileError, FileResult, StrResult};
-use typst::foundations::{Bytes, Datetime};
+use typst::foundations::{Bytes, Datetime, Dict, IntoValue};
use typst::syntax::{FileId, Source, VirtualPath};
use typst::text::{Font, FontBook};
use typst::{Library, World};
@@ -68,14 +68,25 @@ impl SystemWorld {
// Resolve the virtual path of the main file within the project root.
let main_path = VirtualPath::within_root(&input, &root)
- .ok_or("input file must be contained in project root")?;
+ .ok_or("source file must be contained in project root")?;
+
+ let library = {
+ // Convert the input pairs to a dictionary.
+ let inputs: Dict = command
+ .inputs
+ .iter()
+ .map(|(k, v)| (k.as_str().into(), v.as_str().into_value()))
+ .collect();
+
+ Library::builder().with_inputs(inputs).build()
+ };
Ok(Self {
workdir: std::env::current_dir().ok(),
input,
root,
main: FileId::new(None, main_path),
- library: Prehashed::new(Library::build()),
+ library: Prehashed::new(library),
book: Prehashed::new(searcher.book),
fonts: searcher.fonts,
slots: RwLock::new(HashMap::new()),
diff --git a/crates/typst-docs/src/lib.rs b/crates/typst-docs/src/lib.rs
index 444dda32..ad40b987 100644
--- a/crates/typst-docs/src/lib.rs
+++ b/crates/typst-docs/src/lib.rs
@@ -55,7 +55,7 @@ static GROUPS: Lazy<Vec<GroupData>> = Lazy::new(|| {
});
static LIBRARY: Lazy<Prehashed<Library>> = Lazy::new(|| {
- let mut lib = Library::build();
+ let mut lib = Library::default();
lib.styles
.set(PageElem::set_width(Smart::Custom(Abs::pt(240.0).into())));
lib.styles.set(PageElem::set_height(Smart::Auto));
diff --git a/crates/typst/src/foundations/mod.rs b/crates/typst/src/foundations/mod.rs
index 4ddc2162..04380bee 100644
--- a/crates/typst/src/foundations/mod.rs
+++ b/crates/typst/src/foundations/mod.rs
@@ -83,7 +83,7 @@ use crate::syntax::Spanned;
pub static FOUNDATIONS: Category;
/// Hook up all `foundations` definitions.
-pub(super) fn define(global: &mut Scope) {
+pub(super) fn define(global: &mut Scope, inputs: Dict) {
global.category(FOUNDATIONS);
global.define_type::<bool>();
global.define_type::<i64>();
@@ -110,7 +110,7 @@ pub(super) fn define(global: &mut Scope) {
global.define_func::<eval>();
global.define_func::<style>();
global.define_module(calc::module());
- global.define_module(sys::module());
+ global.define_module(sys::module(inputs));
}
/// Fails with an error.
diff --git a/crates/typst/src/foundations/sys.rs b/crates/typst/src/foundations/sys.rs
index 3561842e..7c128104 100644
--- a/crates/typst/src/foundations/sys.rs
+++ b/crates/typst/src/foundations/sys.rs
@@ -1,9 +1,9 @@
//! System-related things.
-use crate::foundations::{Module, Scope, Version};
+use crate::foundations::{Dict, Module, Scope, Version};
/// A module with system-related things.
-pub fn module() -> Module {
+pub fn module(inputs: Dict) -> Module {
let mut scope = Scope::deduplicating();
scope.define(
"version",
@@ -13,5 +13,6 @@ pub fn module() -> Module {
env!("CARGO_PKG_VERSION_PATCH").parse::<u32>().unwrap(),
]),
);
+ scope.define("inputs", inputs);
Module::new("sys", scope)
}
diff --git a/crates/typst/src/lib.rs b/crates/typst/src/lib.rs
index 303d5183..ffe55040 100644
--- a/crates/typst/src/lib.rs
+++ b/crates/typst/src/lib.rs
@@ -66,7 +66,7 @@ use crate::diag::{warning, FileResult, SourceDiagnostic, SourceResult};
use crate::engine::{Engine, Route};
use crate::eval::Tracer;
use crate::foundations::{
- Array, Bytes, Content, Datetime, Module, Scope, StyleChain, Styles,
+ Array, Bytes, Content, Datetime, Dict, Module, Scope, StyleChain, Styles,
};
use crate::introspection::{Introspector, Locator};
use crate::layout::{Align, Dir, LayoutRoot};
@@ -252,25 +252,48 @@ pub struct Library {
}
impl Library {
- /// Construct the standard library.
- pub fn build() -> Self {
- let math = math::module();
- let global = global(math.clone());
- Self { global, math, styles: Styles::new() }
+ /// Create a new builder for a library.
+ pub fn builder() -> LibraryBuilder {
+ LibraryBuilder::default()
}
}
impl Default for Library {
+ /// Constructs the standard library with the default configuration.
fn default() -> Self {
- Self::build()
+ Self::builder().build()
+ }
+}
+
+/// Configurable builder for the standard library.
+///
+/// This struct is created by [`Library::builder`].
+#[derive(Debug, Clone, Default)]
+pub struct LibraryBuilder {
+ inputs: Option<Dict>,
+}
+
+impl LibraryBuilder {
+ /// Configure the inputs visible through `sys.inputs`.
+ pub fn with_inputs(mut self, inputs: Dict) -> Self {
+ self.inputs = Some(inputs);
+ self
+ }
+
+ /// Consumes the builder and returns a `Library`.
+ pub fn build(self) -> Library {
+ let math = math::module();
+ let inputs = self.inputs.unwrap_or_default();
+ let global = global(math.clone(), inputs);
+ Library { global, math, styles: Styles::new() }
}
}
/// Construct the module with global definitions.
#[tracing::instrument(skip_all)]
-fn global(math: Module) -> Module {
+fn global(math: Module, inputs: Dict) -> Module {
let mut global = Scope::deduplicating();
- self::foundations::define(&mut global);
+ self::foundations::define(&mut global, inputs);
self::model::define(&mut global);
self::text::define(&mut global);
global.reset_category();
diff --git a/docs/reference/groups.yml b/docs/reference/groups.yml
index 5ae564b2..16ded697 100644
--- a/docs/reference/groups.yml
+++ b/docs/reference/groups.yml
@@ -129,9 +129,19 @@
details: |
Module for system interactions.
- Currently, this module defines a single item: The `sys.version` constant
- (of type [`version`]($version)), that specifies the currently active
- Typst compiler version.
+ This module defines the following items:
+
+ - The `sys.version` constant (of type [`version`]($version)) that specifies
+ the currently active Typst compiler version.
+
+ - The `sys.inputs` [dictionary]($dictionary), which makes external inputs
+ available to the project. An input specified in the command line as
+ `--input key=value` becomes available under `sys.inputs.key` as
+ `{"value"}`. To include spaces in the value, it may be enclosed with
+ single or double quotes.
+
+ The value is always of type [string]($str). More complex data
+ may be parsed manually using functions like [`json.decode`]($json.decode).
- name: sym
title: General
diff --git a/tests/fuzz/src/compile.rs b/tests/fuzz/src/compile.rs
index deb71778..c6345051 100644
--- a/tests/fuzz/src/compile.rs
+++ b/tests/fuzz/src/compile.rs
@@ -24,7 +24,7 @@ impl FuzzWorld {
let font = Font::new(FONT.into(), 0).unwrap();
let book = FontBook::from_fonts([&font]);
Self {
- library: Prehashed::new(Library::build()),
+ library: Prehashed::new(Library::default()),
book: Prehashed::new(book),
font,
source: Source::detached(text),
diff --git a/tests/src/benches.rs b/tests/src/benches.rs
index 5ec0be61..6d5f3133 100644
--- a/tests/src/benches.rs
+++ b/tests/src/benches.rs
@@ -91,7 +91,7 @@ impl BenchWorld {
let book = FontBook::from_fonts([&font]);
Self {
- library: Prehashed::new(Library::build()),
+ library: Prehashed::new(Library::default()),
book: Prehashed::new(book),
font,
source: Source::detached(TEXT),
diff --git a/tests/src/tests.rs b/tests/src/tests.rs
index f0678309..5e6b7b40 100644
--- a/tests/src/tests.rs
+++ b/tests/src/tests.rs
@@ -192,7 +192,7 @@ fn library() -> Library {
// Set page width to 120pt with 10pt margins, so that the inner page is
// exactly 100pt wide. Page height is unbounded and font size is 10pt so
// that it multiplies to nice round numbers.
- let mut lib = Library::build();
+ let mut lib = Library::default();
lib.styles
.set(PageElem::set_width(Smart::Custom(Abs::pt(120.0).into())));
lib.styles.set(PageElem::set_height(Smart::Auto));