summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock193
-rw-r--r--cli/Cargo.toml2
-rw-r--r--cli/src/main.rs237
3 files changed, 309 insertions, 123 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 20d95967..7394da54 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -27,6 +27,46 @@ dependencies = [
]
[[package]]
+name = "anstream"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "342258dd14006105c2b75ab1bd7543a03bdf0cfc94383303ac212a04939dff6f"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-wincon",
+ "concolor-override",
+ "concolor-query",
+ "is-terminal",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7d1bb534e9efed14f3e5f44e7dd1a4f709384023a4165199a4241e18dff0116"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3127af6145b149f3287bb9a0d10ad9c5692dba8c53ad48285e5bec4063834fa"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
name = "arrayref"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -143,6 +183,48 @@ dependencies = [
]
[[package]]
+name = "clap"
+version = "4.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+ "once_cell",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "bitflags",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.11",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
+
+[[package]]
name = "codespan-reporting"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -180,6 +262,21 @@ dependencies = [
]
[[package]]
+name = "concolor-override"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a855d4a1978dc52fb0536a04d384c2c0c1aa273597f08b77c8c4d3b2eec6037f"
+
+[[package]]
+name = "concolor-query"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf"
+dependencies = [
+ "windows-sys 0.45.0",
+]
+
+[[package]]
name = "core-foundation-sys"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -258,7 +355,7 @@ dependencies = [
"proc-macro2",
"quote",
"scratch",
- "syn 2.0.4",
+ "syn 2.0.11",
]
[[package]]
@@ -275,7 +372,7 @@ checksum = "631569015d0d8d54e6c241733f944042623ab6df7bc3be7466874b05fcdb1c5f"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.4",
+ "syn 2.0.11",
]
[[package]]
@@ -334,6 +431,27 @@ dependencies = [
]
[[package]]
+name = "errno"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
name = "fancy-regex"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -459,6 +577,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
+name = "hermit-abi"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
+
+[[package]]
name = "hypher"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -578,6 +702,29 @@ dependencies = [
]
[[package]]
+name = "io-lifetimes"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "256017f749ab3117e93acb91063009e1f1bb56d03965b14c2c8df4eb02c524d8"
+dependencies = [
+ "hermit-abi",
+ "io-lifetimes",
+ "rustix",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
name = "isolang"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -670,6 +817,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
+name = "linux-raw-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd550e73688e6d578f0ac2119e32b797a327631a42f9433e59d02e139c8df60d"
+
+[[package]]
name = "lipsum"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1007,6 +1160,20 @@ dependencies = [
]
[[package]]
+name = "rustix"
+version = "0.37.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e78cc525325c06b4a7ff02db283472f3c042b7ff0c391f96c6d5ac6f4f91b75"
+dependencies = [
+ "bitflags",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
name = "rustversion"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1075,7 +1242,7 @@ checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.4",
+ "syn 2.0.11",
]
[[package]]
@@ -1129,6 +1296,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
name = "strum"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1190,9 +1363,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.4"
+version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c622ae390c9302e214c31013517c2061ecb2699935882c60a9b37f82f8625ae"
+checksum = "21e3787bb71465627110e7d87ed4faaa36c1f61042ee67badb9e2ef173accc40"
dependencies = [
"proc-macro2",
"quote",
@@ -1252,7 +1425,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.4",
+ "syn 2.0.11",
]
[[package]]
@@ -1358,6 +1531,7 @@ name = "typst-cli"
version = "0.0.0"
dependencies = [
"chrono",
+ "clap",
"codespan-reporting",
"comemo",
"dirs",
@@ -1365,7 +1539,6 @@ dependencies = [
"memmap2",
"notify",
"once_cell",
- "pico-args",
"same-file",
"siphasher",
"typst",
@@ -1586,6 +1759,12 @@ dependencies = [
]
[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index e4fbb74c..f484bf32 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -23,10 +23,10 @@ elsa = "1.7"
memmap2 = "0.5"
notify = "5"
once_cell = "1"
-pico-args = "0.4"
same-file = "1"
siphasher = "0.3"
walkdir = "2"
+clap = { version = "4.2.1", features = ["derive"] }
[features]
default = ["embed-fonts"]
diff --git a/cli/src/main.rs b/cli/src/main.rs
index 514beaa2..021012c7 100644
--- a/cli/src/main.rs
+++ b/cli/src/main.rs
@@ -6,6 +6,7 @@ use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::process;
+use clap::{ArgAction, Parser, Subcommand};
use codespan_reporting::diagnostic::{Diagnostic, Label};
use codespan_reporting::term::{self, termcolor};
use comemo::Prehashed;
@@ -13,7 +14,6 @@ use elsa::FrozenVec;
use memmap2::Mmap;
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
use once_cell::unsync::OnceCell;
-use pico_args::Arguments;
use same_file::{is_same_file, Handle};
use siphasher::sip128::{Hasher128, SipHasher};
use termcolor::{ColorChoice, StandardStream, WriteColor};
@@ -28,141 +28,156 @@ use walkdir::WalkDir;
type CodespanResult<T> = Result<T, CodespanError>;
type CodespanError = codespan_reporting::files::Error;
+const TYPST_VERSION: &str = env!("TYPST_VERSION");
+
+/// typst creates PDF files from .typ files
+#[derive(Debug, Clone, Parser)]
+#[clap(name = "typst", version = TYPST_VERSION, author)]
+pub struct CliArguments {
+ /// Add additional directories to search for fonts
+ #[clap(long = "font-path", value_name = "DIR", action = ArgAction::Append)]
+ font_paths: Vec<PathBuf>,
+
+ /// Configure the root for absolute paths
+ #[clap(long = "root", value_name = "DIR")]
+ root: Option<PathBuf>,
+
+ /// The typst command to run
+ #[command(subcommand)]
+ command: Command,
+}
+
/// What to do.
+#[derive(Debug, Clone, Subcommand)]
+#[command()]
enum Command {
+ /// Compiles the input file into a PDF file
Compile(CompileCommand),
+
+ /// Watches the input file and recompiles on changes
+ Watch(WatchCommand),
+
+ /// List all discovered fonts in system and custom font paths
Fonts(FontsCommand),
}
-/// Compile a .typ file into a PDF file.
-struct CompileCommand {
+/// Compiles the input file into a PDF file
+#[derive(Debug, Clone, Parser)]
+pub struct CompileCommand {
+ /// Path to input Typst file
input: PathBuf,
- output: PathBuf,
- root: Option<PathBuf>,
- watch: bool,
- font_paths: Vec<PathBuf>,
+
+ /// Path to output PDF file
+ output: Option<PathBuf>,
}
-const HELP: &'static str = "\
-typst creates PDF files from .typ files
+/// Watches the input file and recompiles on changes
+#[derive(Debug, Clone, Parser)]
+pub struct WatchCommand {
+ /// Path to input Typst file
+ input: PathBuf,
-USAGE:
- typst [OPTIONS] <input.typ> [output.pdf]
- typst [SUBCOMMAND] ...
+ /// Path to output PDF file
+ output: Option<PathBuf>,
+}
-ARGS:
- <input.typ> Path to input Typst file
- [output.pdf] Path to output PDF file
+/// List all discovered fonts in system and custom font paths
+#[derive(Debug, Clone, Parser)]
+pub struct FontsCommand {
+ /// Add additional directories to search for fonts
+ #[arg(long)]
+ variants: bool,
+}
-OPTIONS:
- -h, --help Print this help
- -V, --version Print the CLI's version
- -w, --watch Watch the inputs and recompile on changes
- --font-path <dir> Add additional directories to search for fonts
- --root <dir> Configure the root for absolute paths
+/// A summary of the input arguments relevant to compilation.
+struct CompileSettings {
+ /// The path to the input file.
+ input: PathBuf,
-SUBCOMMANDS:
- --fonts List all discovered fonts in system and custom font paths
-";
+ /// The path to the output file.
+ output: PathBuf,
-/// List discovered system fonts.
-struct FontsCommand {
+ /// Whether to watch the input files for changes.
+ watch: bool,
+
+ /// The root directory for absolute paths.
+ root: Option<PathBuf>,
+
+ /// The paths to search for fonts.
font_paths: Vec<PathBuf>,
- variants: bool,
}
-const HELP_FONTS: &'static str = "\
-typst --fonts lists all discovered fonts in system and custom font paths
-
-USAGE:
- typst --fonts [OPTIONS]
+impl CompileSettings {
+ /// Create a new compile settings from the field values.
+ pub fn new(
+ input: PathBuf,
+ output: Option<PathBuf>,
+ watch: bool,
+ root: Option<PathBuf>,
+ font_paths: Vec<PathBuf>,
+ ) -> Self {
+ let output = match output {
+ Some(path) => path,
+ None => input.with_extension("pdf"),
+ };
-OPTIONS:
- -h, --help Print this help
- --font-path <dir> Add additional directories to search for fonts
- --variants Also list style variants of each font family
-";
+ Self { input, output, watch, root, font_paths }
+ }
-/// Entry point.
-fn main() {
- let command = parse_args();
- let ok = command.is_ok();
- if let Err(msg) = command.and_then(dispatch) {
- print_error(&msg).unwrap();
- if !ok {
- println!("\nfor more information, try --help");
- }
- process::exit(1);
+ /// Create a new compile settings from the CLI arguments and a compile command.
+ ///
+ /// # Panics
+ /// Panics if the command is not a compile or watch command.
+ pub fn with_arguments(args: CliArguments) -> Self {
+ let (input, output, watch) = match args.command {
+ Command::Compile(command) => (command.input, command.output, false),
+ Command::Watch(command) => (command.input, command.output, true),
+ _ => unreachable!(),
+ };
+ Self::new(input, output, watch, args.root, args.font_paths)
}
}
-/// Parse command line arguments.
-fn parse_args() -> StrResult<Command> {
- let mut args = Arguments::from_env();
- if args.contains(["-V", "--version"]) {
- print_version();
- }
+struct FontsSettings {
+ /// The font paths
+ font_paths: Vec<PathBuf>,
- let help = args.contains(["-h", "--help"]);
- let font_paths = args.values_from_str("--font-path").unwrap();
+ /// Wether to include font variants
+ variants: bool,
+}
- let command = if args.contains("--fonts") {
- if help {
- print_help(HELP_FONTS);
- }
+impl FontsSettings {
+ /// Create font settings from the field values.
+ pub fn new(font_paths: Vec<PathBuf>, variants: bool) -> Self {
+ Self { font_paths, variants }
+ }
- Command::Fonts(FontsCommand { font_paths, variants: args.contains("--variants") })
- } else {
- if help {
- print_help(HELP);
+ /// Create a new font settings from the CLI arguments.
+ ///
+ /// # Panics
+ /// Panics if the command is not a fonts command.
+ pub fn with_arguments(args: CliArguments) -> Self {
+ match args.command {
+ Command::Fonts(command) => Self::new(args.font_paths, command.variants),
+ _ => unreachable!(),
}
-
- let root = args.opt_value_from_str("--root").map_err(|_| "missing root path")?;
- let watch = args.contains(["-w", "--watch"]);
- let (input, output) = parse_input_output(&mut args, "pdf")?;
- Command::Compile(CompileCommand { input, output, watch, root, font_paths })
- };
-
- // Don't allow excess arguments.
- let rest = args.finish();
- if !rest.is_empty() {
- Err(format!("unexpected argument{}", if rest.len() > 1 { "s" } else { "" }))?;
}
-
- Ok(command)
}
-/// Parse two freestanding path arguments, with the output path being optional.
-/// If it is omitted, it is determined from the input path's file stem plus the
-/// given extension.
-fn parse_input_output(args: &mut Arguments, ext: &str) -> StrResult<(PathBuf, PathBuf)> {
- let input: PathBuf = args.free_from_str().map_err(|_| "missing input file")?;
- let output = match args.opt_free_from_str().ok().flatten() {
- Some(output) => output,
- None => {
- let name = input.file_name().ok_or("source path does not point to a file")?;
- Path::new(name).with_extension(ext)
+/// Entry point.
+fn main() {
+ let arguments = CliArguments::parse();
+
+ let res = match &arguments.command {
+ Command::Compile(_) | Command::Watch(_) => {
+ compile(CompileSettings::with_arguments(arguments))
}
+ Command::Fonts(_) => fonts(FontsSettings::with_arguments(arguments)),
};
- // Ensure that the source file is not overwritten.
- if is_same_file(&input, &output).unwrap_or(false) {
- Err("source and destination files are the same")?;
+ if let Err(msg) = res {
+ print_error(&msg).expect("failed to print error");
}
-
- Ok((input, output))
-}
-
-/// Print a help string and quit.
-fn print_help(help: &'static str) -> ! {
- print!("{help}");
- std::process::exit(0);
-}
-
-/// Print the version hash and quit.
-fn print_version() -> ! {
- println!("typst {}", env!("TYPST_VERSION"));
- std::process::exit(0);
}
/// Print an application-level error (independent from a source file).
@@ -177,16 +192,8 @@ fn print_error(msg: &str) -> io::Result<()> {
writeln!(w, ": {msg}.")
}
-/// Dispatch a command.
-fn dispatch(command: Command) -> StrResult<()> {
- match command {
- Command::Compile(command) => compile(command),
- Command::Fonts(command) => fonts(command),
- }
-}
-
/// Execute a compilation command.
-fn compile(command: CompileCommand) -> StrResult<()> {
+fn compile(command: CompileSettings) -> StrResult<()> {
let root = if let Some(root) = &command.root {
root.clone()
} else if let Some(dir) = command
@@ -254,7 +261,7 @@ fn compile(command: CompileCommand) -> StrResult<()> {
}
/// Compile a single time.
-fn compile_once(world: &mut SystemWorld, command: &CompileCommand) -> StrResult<bool> {
+fn compile_once(world: &mut SystemWorld, command: &CompileSettings) -> StrResult<bool> {
status(command, Status::Compiling).unwrap();
world.reset();
@@ -280,7 +287,7 @@ fn compile_once(world: &mut SystemWorld, command: &CompileCommand) -> StrResult<
}
/// Clear the terminal and render the status message.
-fn status(command: &CompileCommand, status: Status) -> io::Result<()> {
+fn status(command: &CompileSettings, status: Status) -> io::Result<()> {
if !command.watch {
return Ok(());
}
@@ -373,7 +380,7 @@ fn print_diagnostics(
}
/// Execute a font listing command.
-fn fonts(command: FontsCommand) -> StrResult<()> {
+fn fonts(command: FontsSettings) -> StrResult<()> {
let mut searcher = FontSearcher::new();
searcher.search_system();
for path in &command.font_paths {