mirror of
https://github.com/typst/typst
synced 2025-07-13 15:42:53 +08:00
Tidy up CLI
This commit is contained in:
parent
8aa0ae197d
commit
88553fe3c0
35
cli/build.rs
35
cli/build.rs
@ -7,23 +7,6 @@ use clap::{CommandFactory, ValueEnum};
|
|||||||
use clap_complete::{generate_to, Shell};
|
use clap_complete::{generate_to, Shell};
|
||||||
use clap_mangen::Man;
|
use clap_mangen::Man;
|
||||||
|
|
||||||
pub fn typst_version() -> String {
|
|
||||||
if let Some(version) = option_env!("TYPST_VERSION") {
|
|
||||||
return version.to_owned();
|
|
||||||
}
|
|
||||||
|
|
||||||
let pkg = env!("CARGO_PKG_VERSION");
|
|
||||||
let hash = Command::new("git")
|
|
||||||
.args(["rev-parse", "HEAD"])
|
|
||||||
.output()
|
|
||||||
.ok()
|
|
||||||
.filter(|output| output.status.success())
|
|
||||||
.and_then(|output| String::from_utf8(output.stdout.get(..8)?.into()).ok())
|
|
||||||
.unwrap_or_else(|| "unknown hash".into());
|
|
||||||
|
|
||||||
format!("{pkg} ({hash})")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[path = "src/args.rs"]
|
#[path = "src/args.rs"]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
mod args;
|
mod args;
|
||||||
@ -57,3 +40,21 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Also used by `args.rs`.
|
||||||
|
fn typst_version() -> String {
|
||||||
|
if let Some(version) = option_env!("TYPST_VERSION") {
|
||||||
|
return version.to_owned();
|
||||||
|
}
|
||||||
|
|
||||||
|
let pkg = env!("CARGO_PKG_VERSION");
|
||||||
|
let hash = Command::new("git")
|
||||||
|
.args(["rev-parse", "HEAD"])
|
||||||
|
.output()
|
||||||
|
.ok()
|
||||||
|
.filter(|output| output.status.success())
|
||||||
|
.and_then(|output| String::from_utf8(output.stdout.get(..8)?.into()).ok())
|
||||||
|
.unwrap_or_else(|| "unknown hash".into());
|
||||||
|
|
||||||
|
format!("{pkg} ({hash})")
|
||||||
|
}
|
||||||
|
@ -18,7 +18,8 @@ pub struct CliArguments {
|
|||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
pub command: Command,
|
pub command: Command,
|
||||||
|
|
||||||
/// Sets the level of verbosity: 0 = none, 1 = warning & error, 2 = info, 3 = debug, 4 = trace
|
/// Sets the level of logging verbosity:
|
||||||
|
/// -v = warning & error, -vv = info, -vvv = debug, -vvvv = trace
|
||||||
#[clap(short, long, action = ArgAction::Count)]
|
#[clap(short, long, action = ArgAction::Count)]
|
||||||
pub verbosity: u8,
|
pub verbosity: u8,
|
||||||
}
|
}
|
||||||
@ -68,8 +69,7 @@ pub struct CompileCommand {
|
|||||||
#[arg(long = "open")]
|
#[arg(long = "open")]
|
||||||
pub open: Option<Option<String>>,
|
pub open: Option<Option<String>>,
|
||||||
|
|
||||||
/// Produces a flamegraph of the compilation process and saves it to the
|
/// Produces a flamegraph of the compilation process
|
||||||
/// given file or to `flamegraph.svg` in the current working directory.
|
|
||||||
#[arg(long = "flamegraph", value_name = "OUTPUT_SVG")]
|
#[arg(long = "flamegraph", value_name = "OUTPUT_SVG")]
|
||||||
pub flamegraph: Option<Option<PathBuf>>,
|
pub flamegraph: Option<Option<PathBuf>>,
|
||||||
}
|
}
|
||||||
|
203
cli/src/main.rs
203
cli/src/main.rs
@ -7,7 +7,6 @@ use std::fs::{self, File};
|
|||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process;
|
|
||||||
|
|
||||||
use atty::Stream;
|
use atty::Stream;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
@ -30,102 +29,18 @@ use typst::World;
|
|||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use crate::args::{CliArguments, Command, CompileCommand};
|
use crate::args::{CliArguments, Command, CompileCommand};
|
||||||
use crate::trace::init_tracing;
|
|
||||||
|
|
||||||
type CodespanResult<T> = Result<T, CodespanError>;
|
type CodespanResult<T> = Result<T, CodespanError>;
|
||||||
type CodespanError = codespan_reporting::files::Error;
|
type CodespanError = codespan_reporting::files::Error;
|
||||||
|
|
||||||
pub fn typst_version() -> &'static str {
|
|
||||||
env!("TYPST_VERSION")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A summary of the input arguments relevant to compilation.
|
|
||||||
struct CompileSettings {
|
|
||||||
/// The path to the input file.
|
|
||||||
input: PathBuf,
|
|
||||||
|
|
||||||
/// The path to the output file.
|
|
||||||
output: PathBuf,
|
|
||||||
|
|
||||||
/// 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>,
|
|
||||||
|
|
||||||
/// The open command to use.
|
|
||||||
open: Option<Option<String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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>,
|
|
||||||
open: Option<Option<String>>,
|
|
||||||
) -> Self {
|
|
||||||
let output = match output {
|
|
||||||
Some(path) => path,
|
|
||||||
None => input.with_extension("pdf"),
|
|
||||||
};
|
|
||||||
Self { input, output, watch, root, font_paths, open }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 watch = matches!(args.command, Command::Watch(_));
|
|
||||||
let CompileCommand { input, output, open, .. } = match args.command {
|
|
||||||
Command::Compile(command) => command,
|
|
||||||
Command::Watch(command) => command,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
Self::new(input, output, watch, args.root, args.font_paths, open)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FontsSettings {
|
|
||||||
/// The font paths
|
|
||||||
font_paths: Vec<PathBuf>,
|
|
||||||
|
|
||||||
/// Whether to include font variants
|
|
||||||
variants: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FontsSettings {
|
|
||||||
/// Create font settings from the field values.
|
|
||||||
pub fn new(font_paths: Vec<PathBuf>, variants: bool) -> Self {
|
|
||||||
Self { font_paths, variants }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Entry point.
|
/// Entry point.
|
||||||
fn main() {
|
fn main() {
|
||||||
let arguments = CliArguments::parse();
|
let arguments = CliArguments::parse();
|
||||||
let _guard = match init_tracing(&arguments) {
|
let _guard = match crate::trace::init_tracing(&arguments) {
|
||||||
Ok(guard) => guard,
|
Ok(guard) => guard,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("failed to initialize tracing, reason: {}", err);
|
eprintln!("failed to initialize tracing {}", err);
|
||||||
return;
|
None
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -153,6 +68,90 @@ fn print_error(msg: &str) -> io::Result<()> {
|
|||||||
writeln!(w, ": {msg}.")
|
writeln!(w, ": {msg}.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used by `args.rs`.
|
||||||
|
fn typst_version() -> &'static str {
|
||||||
|
env!("TYPST_VERSION")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A summary of the input arguments relevant to compilation.
|
||||||
|
struct CompileSettings {
|
||||||
|
/// The path to the input file.
|
||||||
|
input: PathBuf,
|
||||||
|
|
||||||
|
/// The path to the output file.
|
||||||
|
output: PathBuf,
|
||||||
|
|
||||||
|
/// 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>,
|
||||||
|
|
||||||
|
/// The open command to use.
|
||||||
|
open: Option<Option<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompileSettings {
|
||||||
|
/// Create a new compile settings from the field values.
|
||||||
|
fn new(
|
||||||
|
input: PathBuf,
|
||||||
|
output: Option<PathBuf>,
|
||||||
|
watch: bool,
|
||||||
|
root: Option<PathBuf>,
|
||||||
|
font_paths: Vec<PathBuf>,
|
||||||
|
open: Option<Option<String>>,
|
||||||
|
) -> Self {
|
||||||
|
let output = match output {
|
||||||
|
Some(path) => path,
|
||||||
|
None => input.with_extension("pdf"),
|
||||||
|
};
|
||||||
|
Self { input, output, watch, root, font_paths, open }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
fn with_arguments(args: CliArguments) -> Self {
|
||||||
|
let watch = matches!(args.command, Command::Watch(_));
|
||||||
|
let CompileCommand { input, output, open, .. } = match args.command {
|
||||||
|
Command::Compile(command) => command,
|
||||||
|
Command::Watch(command) => command,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
Self::new(input, output, watch, args.root, args.font_paths, open)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FontsSettings {
|
||||||
|
/// The font paths
|
||||||
|
font_paths: Vec<PathBuf>,
|
||||||
|
|
||||||
|
/// Whether to include font variants
|
||||||
|
variants: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FontsSettings {
|
||||||
|
/// Create font settings from the field values.
|
||||||
|
fn new(font_paths: Vec<PathBuf>, variants: bool) -> Self {
|
||||||
|
Self { font_paths, variants }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new font settings from the CLI arguments.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
/// Panics if the command is not a fonts command.
|
||||||
|
fn with_arguments(args: CliArguments) -> Self {
|
||||||
|
match args.command {
|
||||||
|
Command::Fonts(command) => Self::new(args.font_paths, command.variants),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Execute a compilation command.
|
/// Execute a compilation command.
|
||||||
fn compile(mut command: CompileSettings) -> StrResult<()> {
|
fn compile(mut command: CompileSettings) -> StrResult<()> {
|
||||||
let root = if let Some(root) = &command.root {
|
let root = if let Some(root) = &command.root {
|
||||||
@ -173,10 +172,11 @@ fn compile(mut command: CompileSettings) -> StrResult<()> {
|
|||||||
let mut world = SystemWorld::new(root, &command.font_paths);
|
let mut world = SystemWorld::new(root, &command.font_paths);
|
||||||
|
|
||||||
// Perform initial compilation.
|
// Perform initial compilation.
|
||||||
let failed = compile_once(&mut world, &command)?;
|
let ok = compile_once(&mut world, &command)?;
|
||||||
|
|
||||||
// open the file if requested, this must be done on the first **successful** compilation
|
// Open the file if requested, this must be done on the first **successful**
|
||||||
if !failed {
|
// compilation.
|
||||||
|
if ok {
|
||||||
if let Some(open) = command.open.take() {
|
if let Some(open) = command.open.take() {
|
||||||
open_file(open.as_deref(), &command.output)?;
|
open_file(open.as_deref(), &command.output)?;
|
||||||
}
|
}
|
||||||
@ -184,8 +184,8 @@ fn compile(mut command: CompileSettings) -> StrResult<()> {
|
|||||||
|
|
||||||
if !command.watch {
|
if !command.watch {
|
||||||
// Return with non-zero exit code in case of error.
|
// Return with non-zero exit code in case of error.
|
||||||
if failed {
|
if !ok {
|
||||||
process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -223,18 +223,23 @@ fn compile(mut command: CompileSettings) -> StrResult<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if recompile {
|
if recompile {
|
||||||
compile_once(&mut world, &command)?;
|
let ok = compile_once(&mut world, &command)?;
|
||||||
comemo::evict(30);
|
comemo::evict(30);
|
||||||
|
|
||||||
// open the file if requested, this must be done on the first **successful** compilation
|
// Ipen the file if requested, this must be done on the first
|
||||||
if let Some(open) = command.open.take() {
|
// **successful** compilation
|
||||||
open_file(open.as_deref(), &command.output)?;
|
if ok {
|
||||||
|
if let Some(open) = command.open.take() {
|
||||||
|
open_file(open.as_deref(), &command.output)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compile a single time.
|
/// Compile a single time.
|
||||||
|
///
|
||||||
|
/// Returns whether it compiled without errors.
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
fn compile_once(world: &mut SystemWorld, command: &CompileSettings) -> StrResult<bool> {
|
fn compile_once(world: &mut SystemWorld, command: &CompileSettings) -> StrResult<bool> {
|
||||||
tracing::info!("Starting compilation");
|
tracing::info!("Starting compilation");
|
||||||
@ -252,7 +257,7 @@ fn compile_once(world: &mut SystemWorld, command: &CompileSettings) -> StrResult
|
|||||||
status(command, Status::Success).unwrap();
|
status(command, Status::Success).unwrap();
|
||||||
|
|
||||||
tracing::info!("Compilation succeeded");
|
tracing::info!("Compilation succeeded");
|
||||||
Ok(false)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print diagnostics.
|
// Print diagnostics.
|
||||||
@ -262,7 +267,7 @@ fn compile_once(world: &mut SystemWorld, command: &CompileSettings) -> StrResult
|
|||||||
.map_err(|_| "failed to print diagnostics")?;
|
.map_err(|_| "failed to print diagnostics")?;
|
||||||
|
|
||||||
tracing::info!("Compilation failed");
|
tracing::info!("Compilation failed");
|
||||||
Ok(true)
|
Ok(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,9 +127,10 @@ pub fn init_tracing(args: &CliArguments) -> Result<Option<TracingGuard>, Error>
|
|||||||
/// Returns the log level filter for the given verbosity level.
|
/// Returns the log level filter for the given verbosity level.
|
||||||
fn level_filter(args: &CliArguments) -> LevelFilter {
|
fn level_filter(args: &CliArguments) -> LevelFilter {
|
||||||
match args.verbosity {
|
match args.verbosity {
|
||||||
0 => LevelFilter::WARN,
|
0 => LevelFilter::OFF,
|
||||||
1 => LevelFilter::INFO,
|
1 => LevelFilter::WARN,
|
||||||
2 => LevelFilter::DEBUG,
|
2 => LevelFilter::INFO,
|
||||||
|
3 => LevelFilter::DEBUG,
|
||||||
_ => LevelFilter::TRACE,
|
_ => LevelFilter::TRACE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user