mirror of
https://github.com/typst/typst
synced 2025-05-20 20:15:29 +08:00
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::fmt::{self, Display, Formatter};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use clap::builder::ValueParser;
|
||||||
use clap::{ArgAction, Args, Parser, Subcommand, ValueEnum};
|
use clap::{ArgAction, Args, Parser, Subcommand, ValueEnum};
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
|
|
||||||
@ -116,6 +117,15 @@ pub struct SharedArgs {
|
|||||||
#[clap(long = "root", env = "TYPST_ROOT", value_name = "DIR")]
|
#[clap(long = "root", env = "TYPST_ROOT", value_name = "DIR")]
|
||||||
pub root: Option<PathBuf>,
|
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
|
/// Adds additional directories to search for fonts
|
||||||
#[clap(
|
#[clap(
|
||||||
long = "font-path",
|
long = "font-path",
|
||||||
@ -134,6 +144,22 @@ pub struct SharedArgs {
|
|||||||
pub diagnostic_format: DiagnosticFormat,
|
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
|
/// Lists all discovered fonts in system and custom font paths
|
||||||
#[derive(Debug, Clone, Parser)]
|
#[derive(Debug, Clone, Parser)]
|
||||||
pub struct FontsCommand {
|
pub struct FontsCommand {
|
||||||
|
@ -7,7 +7,7 @@ use chrono::{DateTime, Datelike, Local};
|
|||||||
use comemo::Prehashed;
|
use comemo::Prehashed;
|
||||||
use ecow::eco_format;
|
use ecow::eco_format;
|
||||||
use typst::diag::{FileError, FileResult, StrResult};
|
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::syntax::{FileId, Source, VirtualPath};
|
||||||
use typst::text::{Font, FontBook};
|
use typst::text::{Font, FontBook};
|
||||||
use typst::{Library, World};
|
use typst::{Library, World};
|
||||||
@ -68,14 +68,25 @@ impl SystemWorld {
|
|||||||
|
|
||||||
// Resolve the virtual path of the main file within the project root.
|
// Resolve the virtual path of the main file within the project root.
|
||||||
let main_path = VirtualPath::within_root(&input, &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 {
|
Ok(Self {
|
||||||
workdir: std::env::current_dir().ok(),
|
workdir: std::env::current_dir().ok(),
|
||||||
input,
|
input,
|
||||||
root,
|
root,
|
||||||
main: FileId::new(None, main_path),
|
main: FileId::new(None, main_path),
|
||||||
library: Prehashed::new(Library::build()),
|
library: Prehashed::new(library),
|
||||||
book: Prehashed::new(searcher.book),
|
book: Prehashed::new(searcher.book),
|
||||||
fonts: searcher.fonts,
|
fonts: searcher.fonts,
|
||||||
slots: RwLock::new(HashMap::new()),
|
slots: RwLock::new(HashMap::new()),
|
||||||
|
@ -55,7 +55,7 @@ static GROUPS: Lazy<Vec<GroupData>> = Lazy::new(|| {
|
|||||||
});
|
});
|
||||||
|
|
||||||
static LIBRARY: Lazy<Prehashed<Library>> = Lazy::new(|| {
|
static LIBRARY: Lazy<Prehashed<Library>> = Lazy::new(|| {
|
||||||
let mut lib = Library::build();
|
let mut lib = Library::default();
|
||||||
lib.styles
|
lib.styles
|
||||||
.set(PageElem::set_width(Smart::Custom(Abs::pt(240.0).into())));
|
.set(PageElem::set_width(Smart::Custom(Abs::pt(240.0).into())));
|
||||||
lib.styles.set(PageElem::set_height(Smart::Auto));
|
lib.styles.set(PageElem::set_height(Smart::Auto));
|
||||||
|
@ -83,7 +83,7 @@ use crate::syntax::Spanned;
|
|||||||
pub static FOUNDATIONS: Category;
|
pub static FOUNDATIONS: Category;
|
||||||
|
|
||||||
/// Hook up all `foundations` definitions.
|
/// 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.category(FOUNDATIONS);
|
||||||
global.define_type::<bool>();
|
global.define_type::<bool>();
|
||||||
global.define_type::<i64>();
|
global.define_type::<i64>();
|
||||||
@ -110,7 +110,7 @@ pub(super) fn define(global: &mut Scope) {
|
|||||||
global.define_func::<eval>();
|
global.define_func::<eval>();
|
||||||
global.define_func::<style>();
|
global.define_func::<style>();
|
||||||
global.define_module(calc::module());
|
global.define_module(calc::module());
|
||||||
global.define_module(sys::module());
|
global.define_module(sys::module(inputs));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fails with an error.
|
/// Fails with an error.
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
//! System-related things.
|
//! System-related things.
|
||||||
|
|
||||||
use crate::foundations::{Module, Scope, Version};
|
use crate::foundations::{Dict, Module, Scope, Version};
|
||||||
|
|
||||||
/// A module with system-related things.
|
/// A module with system-related things.
|
||||||
pub fn module() -> Module {
|
pub fn module(inputs: Dict) -> Module {
|
||||||
let mut scope = Scope::deduplicating();
|
let mut scope = Scope::deduplicating();
|
||||||
scope.define(
|
scope.define(
|
||||||
"version",
|
"version",
|
||||||
@ -13,5 +13,6 @@ pub fn module() -> Module {
|
|||||||
env!("CARGO_PKG_VERSION_PATCH").parse::<u32>().unwrap(),
|
env!("CARGO_PKG_VERSION_PATCH").parse::<u32>().unwrap(),
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
scope.define("inputs", inputs);
|
||||||
Module::new("sys", scope)
|
Module::new("sys", scope)
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ use crate::diag::{warning, FileResult, SourceDiagnostic, SourceResult};
|
|||||||
use crate::engine::{Engine, Route};
|
use crate::engine::{Engine, Route};
|
||||||
use crate::eval::Tracer;
|
use crate::eval::Tracer;
|
||||||
use crate::foundations::{
|
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::introspection::{Introspector, Locator};
|
||||||
use crate::layout::{Align, Dir, LayoutRoot};
|
use crate::layout::{Align, Dir, LayoutRoot};
|
||||||
@ -252,25 +252,48 @@ pub struct Library {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Library {
|
impl Library {
|
||||||
/// Construct the standard library.
|
/// Create a new builder for a library.
|
||||||
pub fn build() -> Self {
|
pub fn builder() -> LibraryBuilder {
|
||||||
let math = math::module();
|
LibraryBuilder::default()
|
||||||
let global = global(math.clone());
|
|
||||||
Self { global, math, styles: Styles::new() }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Library {
|
impl Default for Library {
|
||||||
|
/// Constructs the standard library with the default configuration.
|
||||||
fn default() -> Self {
|
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.
|
/// Construct the module with global definitions.
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
fn global(math: Module) -> Module {
|
fn global(math: Module, inputs: Dict) -> Module {
|
||||||
let mut global = Scope::deduplicating();
|
let mut global = Scope::deduplicating();
|
||||||
self::foundations::define(&mut global);
|
self::foundations::define(&mut global, inputs);
|
||||||
self::model::define(&mut global);
|
self::model::define(&mut global);
|
||||||
self::text::define(&mut global);
|
self::text::define(&mut global);
|
||||||
global.reset_category();
|
global.reset_category();
|
||||||
|
@ -129,9 +129,19 @@
|
|||||||
details: |
|
details: |
|
||||||
Module for system interactions.
|
Module for system interactions.
|
||||||
|
|
||||||
Currently, this module defines a single item: The `sys.version` constant
|
This module defines the following items:
|
||||||
(of type [`version`]($version)), that specifies the currently active
|
|
||||||
Typst compiler version.
|
- 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
|
- name: sym
|
||||||
title: General
|
title: General
|
||||||
|
@ -24,7 +24,7 @@ impl FuzzWorld {
|
|||||||
let font = Font::new(FONT.into(), 0).unwrap();
|
let font = Font::new(FONT.into(), 0).unwrap();
|
||||||
let book = FontBook::from_fonts([&font]);
|
let book = FontBook::from_fonts([&font]);
|
||||||
Self {
|
Self {
|
||||||
library: Prehashed::new(Library::build()),
|
library: Prehashed::new(Library::default()),
|
||||||
book: Prehashed::new(book),
|
book: Prehashed::new(book),
|
||||||
font,
|
font,
|
||||||
source: Source::detached(text),
|
source: Source::detached(text),
|
||||||
|
@ -91,7 +91,7 @@ impl BenchWorld {
|
|||||||
let book = FontBook::from_fonts([&font]);
|
let book = FontBook::from_fonts([&font]);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
library: Prehashed::new(Library::build()),
|
library: Prehashed::new(Library::default()),
|
||||||
book: Prehashed::new(book),
|
book: Prehashed::new(book),
|
||||||
font,
|
font,
|
||||||
source: Source::detached(TEXT),
|
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
|
// 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
|
// exactly 100pt wide. Page height is unbounded and font size is 10pt so
|
||||||
// that it multiplies to nice round numbers.
|
// that it multiplies to nice round numbers.
|
||||||
let mut lib = Library::build();
|
let mut lib = Library::default();
|
||||||
lib.styles
|
lib.styles
|
||||||
.set(PageElem::set_width(Smart::Custom(Abs::pt(120.0).into())));
|
.set(PageElem::set_width(Smart::Custom(Abs::pt(120.0).into())));
|
||||||
lib.styles.set(PageElem::set_height(Smart::Auto));
|
lib.styles.set(PageElem::set_height(Smart::Auto));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user