diff --git a/cli/build.rs b/cli/build.rs index b6f7d6eef..86325e1d1 100644 --- a/cli/build.rs +++ b/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})") +} diff --git a/cli/src/args.rs b/cli/src/args.rs index 699dbb97d..7eb4f4e2d 100644 --- a/cli/src/args.rs +++ b/cli/src/args.rs @@ -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>, - /// 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>, } diff --git a/cli/src/main.rs b/cli/src/main.rs index 4e9bd43e1..eb11cfc67 100644 --- a/cli/src/main.rs +++ b/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 = Result; 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, - - /// The paths to search for fonts. - font_paths: Vec, - - /// The open command to use. - open: Option>, -} - -impl CompileSettings { - /// Create a new compile settings from the field values. - pub fn new( - input: PathBuf, - output: Option, - watch: bool, - root: Option, - font_paths: Vec, - open: Option>, - ) -> 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, - - /// Whether to include font variants - variants: bool, -} - -impl FontsSettings { - /// Create font settings from the field values. - pub fn new(font_paths: Vec, 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, + + /// The paths to search for fonts. + font_paths: Vec, + + /// The open command to use. + open: Option>, +} + +impl CompileSettings { + /// Create a new compile settings from the field values. + fn new( + input: PathBuf, + output: Option, + watch: bool, + root: Option, + font_paths: Vec, + open: Option>, + ) -> 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, + + /// Whether to include font variants + variants: bool, +} + +impl FontsSettings { + /// Create font settings from the field values. + fn new(font_paths: Vec, 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 { 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) } } } diff --git a/cli/src/trace.rs b/cli/src/trace.rs index 0b37c5fcd..06b5668e3 100644 --- a/cli/src/trace.rs +++ b/cli/src/trace.rs @@ -127,9 +127,10 @@ pub fn init_tracing(args: &CliArguments) -> Result, 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, } }