Key/Value data from CLI (#2894)
This commit is contained in:
parent
356bdeba18
commit
22ba6825db
@ -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 {
|
||||
|
@ -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()),
|
||||
|
@ -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));
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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));
|
||||
|
Loading…
Reference in New Issue
Block a user