diff options
| author | Laurenz <ritters_werth@outlook.com> | 2023-12-18 12:18:41 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-12-18 12:18:41 +0100 |
| commit | 22ba6825db3b82e0b0f83ef6052f17289893e385 (patch) | |
| tree | cf69e7ac0eeb49f13ffb0b81e36461e07cb766ba | |
| parent | 356bdeba18153efd4209657182971e22bdaf4db2 (diff) | |
Key/Value data from CLI (#2894)
| -rw-r--r-- | crates/typst-cli/src/args.rs | 26 | ||||
| -rw-r--r-- | crates/typst-cli/src/world.rs | 17 | ||||
| -rw-r--r-- | crates/typst-docs/src/lib.rs | 2 | ||||
| -rw-r--r-- | crates/typst/src/foundations/mod.rs | 4 | ||||
| -rw-r--r-- | crates/typst/src/foundations/sys.rs | 5 | ||||
| -rw-r--r-- | crates/typst/src/lib.rs | 41 | ||||
| -rw-r--r-- | docs/reference/groups.yml | 16 | ||||
| -rw-r--r-- | tests/fuzz/src/compile.rs | 2 | ||||
| -rw-r--r-- | tests/src/benches.rs | 2 | ||||
| -rw-r--r-- | tests/src/tests.rs | 2 |
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)); |
