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_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"]
|
||||
#[allow(dead_code)]
|
||||
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)]
|
||||
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)]
|
||||
pub verbosity: u8,
|
||||
}
|
||||
@ -68,8 +69,7 @@ pub struct CompileCommand {
|
||||
#[arg(long = "open")]
|
||||
pub open: Option<Option<String>>,
|
||||
|
||||
/// Produces a flamegraph of the compilation process and saves it to the
|
||||
/// given file or to `flamegraph.svg` in the current working directory.
|
||||
/// Produces a flamegraph of the compilation process
|
||||
#[arg(long = "flamegraph", value_name = "OUTPUT_SVG")]
|
||||
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::io::{self, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process;
|
||||
|
||||
use atty::Stream;
|
||||
use clap::Parser;
|
||||
@ -30,102 +29,18 @@ use typst::World;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use crate::args::{CliArguments, Command, CompileCommand};
|
||||
use crate::trace::init_tracing;
|
||||
|
||||
type CodespanResult<T> = Result<T, CodespanError>;
|
||||
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.
|
||||
fn main() {
|
||||
let arguments = CliArguments::parse();
|
||||
let _guard = match init_tracing(&arguments) {
|
||||
let _guard = match crate::trace::init_tracing(&arguments) {
|
||||
Ok(guard) => guard,
|
||||
Err(err) => {
|
||||
eprintln!("failed to initialize tracing, reason: {}", err);
|
||||
return;
|
||||
eprintln!("failed to initialize tracing {}", err);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
@ -153,6 +68,90 @@ fn print_error(msg: &str) -> io::Result<()> {
|
||||
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.
|
||||
fn compile(mut command: CompileSettings) -> StrResult<()> {
|
||||
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);
|
||||
|
||||
// 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
|
||||
if !failed {
|
||||
// Open the file if requested, this must be done on the first **successful**
|
||||
// compilation.
|
||||
if ok {
|
||||
if let Some(open) = command.open.take() {
|
||||
open_file(open.as_deref(), &command.output)?;
|
||||
}
|
||||
@ -184,8 +184,8 @@ fn compile(mut command: CompileSettings) -> StrResult<()> {
|
||||
|
||||
if !command.watch {
|
||||
// Return with non-zero exit code in case of error.
|
||||
if failed {
|
||||
process::exit(1);
|
||||
if !ok {
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
@ -223,18 +223,23 @@ fn compile(mut command: CompileSettings) -> StrResult<()> {
|
||||
}
|
||||
|
||||
if recompile {
|
||||
compile_once(&mut world, &command)?;
|
||||
let ok = compile_once(&mut world, &command)?;
|
||||
comemo::evict(30);
|
||||
|
||||
// open the file if requested, this must be done on the first **successful** compilation
|
||||
if let Some(open) = command.open.take() {
|
||||
open_file(open.as_deref(), &command.output)?;
|
||||
// Ipen the file if requested, this must be done on the first
|
||||
// **successful** compilation
|
||||
if ok {
|
||||
if let Some(open) = command.open.take() {
|
||||
open_file(open.as_deref(), &command.output)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compile a single time.
|
||||
///
|
||||
/// Returns whether it compiled without errors.
|
||||
#[tracing::instrument(skip_all)]
|
||||
fn compile_once(world: &mut SystemWorld, command: &CompileSettings) -> StrResult<bool> {
|
||||
tracing::info!("Starting compilation");
|
||||
@ -252,7 +257,7 @@ fn compile_once(world: &mut SystemWorld, command: &CompileSettings) -> StrResult
|
||||
status(command, Status::Success).unwrap();
|
||||
|
||||
tracing::info!("Compilation succeeded");
|
||||
Ok(false)
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
// Print diagnostics.
|
||||
@ -262,7 +267,7 @@ fn compile_once(world: &mut SystemWorld, command: &CompileSettings) -> StrResult
|
||||
.map_err(|_| "failed to print diagnostics")?;
|
||||
|
||||
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.
|
||||
fn level_filter(args: &CliArguments) -> LevelFilter {
|
||||
match args.verbosity {
|
||||
0 => LevelFilter::WARN,
|
||||
1 => LevelFilter::INFO,
|
||||
2 => LevelFilter::DEBUG,
|
||||
0 => LevelFilter::OFF,
|
||||
1 => LevelFilter::WARN,
|
||||
2 => LevelFilter::INFO,
|
||||
3 => LevelFilter::DEBUG,
|
||||
_ => LevelFilter::TRACE,
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user