Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix some errors not being reported on console #963

Merged
merged 2 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 41 additions & 35 deletions src/bin/cargo-msrv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::sync::Arc;
use storyteller::{EventHandler, EventListener, FinishProcessing};
use tracing_appender::rolling::{RollingFileAppender, Rotation};

use cargo_msrv::cli::CargoCli;
use cargo_msrv::cli::{CargoCli, CargoMsrvOpts};
use cargo_msrv::error::CargoMSRVError;
use cargo_msrv::exit_code::ExitCode;
use cargo_msrv::reporter::{
Expand All @@ -18,20 +18,24 @@ use cargo_msrv::{run_app, Context, OutputFormat, TracingOptions, TracingTargetOp

fn main() {
std::process::exit(
match _main(std::env::args_os) {
match setup_opts_and_tracing(std::env::args_os) {
Ok((_guard, exit_code)) => exit_code,
Err(err) => {
tracing::error!("{}", err);
// Don't use `tracing::error!` here because maybe an issue with `tracing` setup is what
// caused this error in the first place
// Additionally `tracing::error!` would not print to console by default, so user would not
// know what went wrong
eprintln!("Setup error: {}", err);
ExitCode::Failure
}
}
.into(),
);
}

fn _main<I: IntoIterator<Item = OsString>, F: FnOnce() -> I + Clone>(
fn setup_opts_and_tracing<I: IntoIterator<Item = OsString>, F: FnOnce() -> I + Clone>(
args: F,
) -> Result<(Option<TracingGuard>, ExitCode), InstanceError> {
) -> Result<(Option<TracingGuard>, ExitCode), SetupError> {
let matches = CargoCli::parse_args(args());
let opts = matches.to_cargo_msrv_cli().to_opts();

Expand All @@ -53,11 +57,10 @@ fn _main<I: IntoIterator<Item = OsString>, F: FnOnce() -> I + Clone>(
guard = Some(init_tracing(&tracing_config)?);
}

let context = Context::try_from(opts).map_err(InstanceError::CargoMsrv)?;
init_and_run(&context).map(|exit_code| (guard, exit_code))
setup_reporter(opts).map(|exit_code| (guard, exit_code))
}

fn init_and_run(ctx: &Context) -> Result<ExitCode, InstanceError> {
fn setup_reporter(opts: CargoMsrvOpts) -> Result<ExitCode, SetupError> {
tracing::info!(
cargo_msrv_version = env!("CARGO_PKG_VERSION"),
"initializing"
Expand All @@ -68,15 +71,15 @@ fn init_and_run(ctx: &Context) -> Result<ExitCode, InstanceError> {

tracing::info!("storyteller channel created");

let output_format = ctx.output_format();
let output_format = opts.shared_opts.user_output_opts.effective_output_format();
foresterre marked this conversation as resolved.
Show resolved Hide resolved
let handler = WrappingHandler::from(output_format);
let finalizer = listener.run_handler(Arc::new(handler));
tracing::info!("storyteller started handler");
tracing::info!("start run_app");
tracing::info!("starting execution");

let res = run_app(ctx, &reporter);
let res = setup_context_and_run(opts, &reporter);

tracing::info!("finished run_app");
tracing::info!("finished execution");

let exit_code = get_exit_code(res, &reporter)?;
disconnect_reporter(reporter)?;
Expand All @@ -85,17 +88,25 @@ fn init_and_run(ctx: &Context) -> Result<ExitCode, InstanceError> {
Ok(exit_code)
}

fn setup_context_and_run(
opts: CargoMsrvOpts,
reporter: &impl Reporter,
) -> Result<(), CargoMSRVError> {
let context = Context::try_from(opts)?;
run_app(&context, reporter)
}

/// Get the exit code from the result of the program's main work unit.
fn get_exit_code(
result: Result<(), CargoMSRVError>,
reporter: &impl Reporter,
) -> Result<ExitCode, InstanceError> {
) -> Result<ExitCode, SetupError> {
Ok(match result {
Ok(_) => ExitCode::Success,
Err(err) => {
reporter
.report_event(TerminateWithFailure::new(err))
.map_err(|_| InstanceError::StorytellerSend)?;
.map_err(|_| SetupError::StorytellerSend)?;

ExitCode::Failure
}
Expand Down Expand Up @@ -150,28 +161,28 @@ impl From<OutputFormat> for WrappingHandler {

/// Disconnect the reporter, signalling that the program is finished, and we can now finish
/// up processing the last user output events.
fn disconnect_reporter(reporter: impl Reporter) -> Result<(), InstanceError> {
fn disconnect_reporter(reporter: impl Reporter) -> Result<(), SetupError> {
reporter
.disconnect()
.map_err(|_| InstanceError::StorytellerDisconnect)?;
.map_err(|_| SetupError::StorytellerDisconnect)?;

tracing::info!("disconnected reporter");

Ok(())
}

/// Wait for the user output processing to finish up it's queue of events by blocking.
fn wait_for_user_output(finalizer: impl FinishProcessing) -> Result<(), InstanceError> {
fn wait_for_user_output(finalizer: impl FinishProcessing) -> Result<(), SetupError> {
finalizer
.finish_processing()
.map_err(|_| InstanceError::StorytellerFinishEventProcessing)?;
.map_err(|_| SetupError::StorytellerFinishEventProcessing)?;

tracing::info!("finished processing unprocessed events");

Ok(())
}

fn init_tracing(tracing_config: &TracingConfig) -> Result<TracingGuard, InstanceError> {
fn init_tracing(tracing_config: &TracingConfig) -> Result<TracingGuard, SetupError> {
let level = tracing_config.level;

match &tracing_config.target {
Expand All @@ -192,7 +203,7 @@ fn init_tracing(tracing_config: &TracingConfig) -> Result<TracingGuard, Instance
fn init_tracing_to_file(
log_folder: impl AsRef<Path>,
level: tracing::Level,
) -> Result<TracingGuard, InstanceError> {
) -> Result<TracingGuard, SetupError> {
let file_appender = RollingFileAppender::new(Rotation::DAILY, log_folder, "cargo-msrv-log");
let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);

Expand All @@ -203,16 +214,16 @@ fn init_tracing_to_file(
.finish();

tracing::subscriber::set_global_default(subscriber)
.map_err(|_| InstanceError::UnableToInitTracing)?;
.map_err(|_| SetupError::UnableToInitTracing)?;

Ok(TracingGuard::NonBlockingGuard(guard))
}

fn init_tracing_to_stdout(level: tracing::Level) -> Result<TracingGuard, InstanceError> {
fn init_tracing_to_stdout(level: tracing::Level) -> Result<TracingGuard, SetupError> {
let subscriber = tracing_subscriber::fmt().with_max_level(level).finish();

tracing::subscriber::set_global_default(subscriber)
.map_err(|_| InstanceError::UnableToInitTracing)?;
.map_err(|_| SetupError::UnableToInitTracing)?;

Ok(TracingGuard::None)
}
Expand All @@ -223,7 +234,7 @@ struct TracingConfig {
}

impl TracingConfig {
fn try_from_options(config: &TracingOptions) -> Result<Self, InstanceError> {
fn try_from_options(config: &TracingOptions) -> Result<Self, SetupError> {
let target = TracingTarget::try_from_option(config.target())?;

Ok(Self {
Expand All @@ -239,7 +250,7 @@ enum TracingTarget {
}

impl TracingTarget {
fn try_from_option(option: &TracingTargetOption) -> Result<Self, InstanceError> {
fn try_from_option(option: &TracingTargetOption) -> Result<Self, SetupError> {
match option {
TracingTargetOption::File => {
let folder = log_folder()?;
Expand All @@ -255,21 +266,16 @@ enum TracingGuard {
None,
}

fn log_folder() -> Result<PathBuf, InstanceError> {
fn log_folder() -> Result<PathBuf, SetupError> {
dirs::data_local_dir()
.map(|path| path.join("cargo-msrv"))
.ok_or(InstanceError::UnableToAccessLogFolder)
.ok_or(SetupError::UnableToAccessLogFolder)
}

/// Error which occurred during setting up the program or shutting it down.
/// Does not cover errors which occur during execution.
#[derive(Debug, thiserror::Error)]
enum InstanceError {
// Only for compat. with `Context::try_from`, which is not as easily converted to this Error type
// of the bin crate.
// For that reason, we do not derive `#[from] CargoMSRVError`, so we don't silently miss calls
// which may produce an `Err(CargoMSRVError)` where we do not want it.
#[error("{0}")]
CargoMsrv(CargoMSRVError),

enum SetupError {
Marcono1234 marked this conversation as resolved.
Show resolved Hide resolved
#[error("Unable to init logger, run with --no-log to try again without logging.")]
UnableToInitTracing,

Expand Down
14 changes: 12 additions & 2 deletions src/cli/shared_opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,21 @@
value_name = "FORMAT",
global = true
)]
pub output_format: OutputFormat,
output_format: OutputFormat,

Check warning on line 36 in src/cli/shared_opts.rs

View check run for this annotation

Codecov / codecov/patch

src/cli/shared_opts.rs#L36

Added line #L36 was not covered by tests

/// Disable user output
#[arg(long, global = true)]
pub no_user_output: bool,
no_user_output: bool,

Check warning on line 40 in src/cli/shared_opts.rs

View check run for this annotation

Codecov / codecov/patch

src/cli/shared_opts.rs#L40

Added line #L40 was not covered by tests
foresterre marked this conversation as resolved.
Show resolved Hide resolved
}

impl UserOutputOpts {
pub fn effective_output_format(&self) -> OutputFormat {
if self.no_user_output {
OutputFormat::None

Check warning on line 46 in src/cli/shared_opts.rs

View check run for this annotation

Codecov / codecov/patch

src/cli/shared_opts.rs#L46

Added line #L46 was not covered by tests
} else {
self.output_format
}
}
}

#[derive(Debug, Args)]
Expand Down
20 changes: 2 additions & 18 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,6 @@ pub enum Context {
}

impl Context {
pub fn output_format(&self) -> OutputFormat {
match self {
Context::Find(ctx) => ctx.user_output.output_format,
Context::List(ctx) => ctx.user_output.output_format,
Context::Set(ctx) => ctx.user_output.output_format,
Context::Show(ctx) => ctx.user_output.output_format,
Context::Verify(ctx) => ctx.user_output.output_format,
}
}

pub fn reporting_name(&self) -> &'static str {
match self {
Context::Find(_) => "find",
Expand Down Expand Up @@ -315,14 +305,8 @@ pub struct UserOutputContext {

impl From<UserOutputOpts> for UserOutputContext {
fn from(opts: UserOutputOpts) -> Self {
if opts.no_user_output {
Self {
output_format: OutputFormat::None,
}
} else {
Self {
output_format: opts.output_format,
}
Self {
output_format: opts.effective_output_format(),
}
}
}
Expand Down
Loading