Tidy up CLI

This commit is contained in:
Laurenz 2023-05-22 15:13:51 +02:00
parent 8aa0ae197d
commit 88553fe3c0
4 changed files with 129 additions and 122 deletions

View File

@ -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})")
}

View File

@ -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>>,
}

View File

@ -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
// 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)
}
}
}

View File

@ -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,
}
}