diff --git a/crates/brioche/src/check.rs b/crates/brioche/src/check.rs index 289c8c8..672f233 100644 --- a/crates/brioche/src/check.rs +++ b/crates/brioche/src/check.rs @@ -1,51 +1,128 @@ +use std::path::PathBuf; use std::process::ExitCode; +use brioche_core::project::ProjectHash; +use brioche_core::project::Projects; use brioche_core::reporter::ConsoleReporterKind; +use brioche_core::reporter::Reporter; +use brioche_core::Brioche; use clap::Parser; use tracing::Instrument; +use crate::consolidate_result; + #[derive(Debug, Parser)] pub struct CheckArgs { #[command(flatten)] - project: super::ProjectArgs, + project: super::MultipleProjectArgs, } pub async fn check(args: CheckArgs) -> anyhow::Result { let (reporter, mut guard) = brioche_core::reporter::start_console_reporter(ConsoleReporterKind::Auto)?; - let brioche = brioche_core::BriocheBuilder::new(reporter).build().await?; + let brioche = brioche_core::BriocheBuilder::new(reporter.clone()) + .build() + .await?; let projects = brioche_core::project::Projects::default(); - let check_future = async { - let project_hash = super::load_project(&brioche, &projects, &args.project).await?; + let mut error_result = Option::None; - let num_lockfiles_updated = projects.commit_dirty_lockfiles().await?; - if num_lockfiles_updated > 0 { - tracing::info!(num_lockfiles_updated, "updated lockfiles"); - } + // Handle the case where no projects and no registries are specified + let projects_path = + if args.project.project.is_empty() && args.project.registry_project.is_empty() { + vec![PathBuf::from(".")] + } else { + args.project.project + }; - let checked = brioche_core::script::check::check(&brioche, &projects, project_hash).await?; + // Loop over the projects + for project_path in projects_path { + let project_name = format!("project '{name}'", name = project_path.display()); - guard.shutdown_console().await; + match projects.load(&brioche, &project_path, true).await { + Ok(project_hash) => { + let result = + run_check(&reporter, &brioche, &projects, project_hash, &project_name).await; + consolidate_result(&reporter, &project_name, result, &mut error_result); + } + Err(e) => { + consolidate_result(&reporter, &project_name, Err(e), &mut error_result); + } + } + } - let result = checked.ensure_ok(brioche_core::script::check::DiagnosticLevel::Message); + // Loop over the registry projects + for registry_project in args.project.registry_project { + let project_name = format!("registry project '{registry_project}'"); - match result { - Ok(()) => { - println!("No errors found 🎉"); - anyhow::Ok(ExitCode::SUCCESS) + match projects + .load_from_registry( + &brioche, + ®istry_project, + &brioche_core::project::Version::Any, + ) + .await + { + Ok(project_hash) => { + let result = + run_check(&reporter, &brioche, &projects, project_hash, &project_name).await; + consolidate_result(&reporter, &project_name, result, &mut error_result); } - Err(diagnostics) => { - diagnostics.write(&brioche.vfs, &mut std::io::stdout())?; - anyhow::Ok(ExitCode::FAILURE) + Err(e) => { + consolidate_result(&reporter, &project_name, Err(e), &mut error_result); } } - }; + } - let exit_code = check_future - .instrument(tracing::info_span!("check")) - .await?; + guard.shutdown_console().await; + + let exit_code = if error_result.is_some() { + ExitCode::FAILURE + } else { + ExitCode::SUCCESS + }; Ok(exit_code) } + +async fn run_check( + reporter: &Reporter, + brioche: &Brioche, + projects: &Projects, + project_hash: ProjectHash, + project_name: &String, +) -> Result { + let num_lockfiles_updated = projects.commit_dirty_lockfiles().await?; + if num_lockfiles_updated > 0 { + tracing::info!(num_lockfiles_updated, "updated lockfiles"); + } + + let result = + async { brioche_core::script::check::check(brioche, projects, project_hash).await } + .instrument(tracing::info_span!("check")) + .await? + .ensure_ok(brioche_core::script::check::DiagnosticLevel::Message); + + match result { + Ok(()) => { + reporter.emit(superconsole::Lines::from_multiline_string( + &format!("No errors found in {project_name} 🎉",), + superconsole::style::ContentStyle::default(), + )); + + Ok(true) + } + Err(diagnostics) => { + let mut output = Vec::new(); + diagnostics.write(&brioche.vfs, &mut output)?; + + reporter.emit(superconsole::Lines::from_multiline_string( + &String::from_utf8(output)?, + superconsole::style::ContentStyle::default(), + )); + + Ok(false) + } + } +} diff --git a/crates/brioche/src/format.rs b/crates/brioche/src/format.rs index 9f258cf..5430177 100644 --- a/crates/brioche/src/format.rs +++ b/crates/brioche/src/format.rs @@ -1,12 +1,14 @@ -use std::{ - path::{Path, PathBuf}, - process::ExitCode, -}; +use std::{path::PathBuf, process::ExitCode}; -use brioche_core::reporter::{ConsoleReporterKind, Reporter}; +use brioche_core::{ + project::{ProjectHash, Projects}, + reporter::{ConsoleReporterKind, Reporter}, +}; use clap::Parser; use tracing::Instrument; +use crate::consolidate_result; + #[derive(Debug, Parser)] pub struct FormatArgs { /// The path to the project directory to format @@ -22,28 +24,32 @@ pub async fn format(args: FormatArgs) -> anyhow::Result { let (reporter, mut guard) = brioche_core::reporter::start_console_reporter(ConsoleReporterKind::Auto)?; + let brioche = brioche_core::BriocheBuilder::new(reporter.clone()) + .build() + .await?; + let projects = brioche_core::project::Projects::default(); + let mut error_result = Option::None; - for project_path in args.project { - match project_format(&reporter, &project_path, args.check).await { - Err(err) => { - reporter.emit(superconsole::Lines::from_multiline_string( - &format!( - "Error occurred while formatting project '{project_path}': {err}", - project_path = project_path.display(), - err = err - ), - superconsole::style::ContentStyle { - foreground_color: Some(superconsole::style::Color::Red), - ..superconsole::style::ContentStyle::default() - }, - )); - error_result = Some(()); + // Loop over the projects + for project_path in args.project { + let project_name = format!("project '{name}'", name = project_path.display()); + + match projects.load(&brioche, &project_path, true).await { + Ok(project_hash) => { + let result = run_format( + &reporter, + &projects, + project_hash, + &project_name, + args.check, + ) + .await; + consolidate_result(&reporter, &project_name, result, &mut error_result); } - Ok(false) => { - error_result = Some(()); + Err(e) => { + consolidate_result(&reporter, &project_name, Err(e), &mut error_result); } - _ => {} } } @@ -58,23 +64,18 @@ pub async fn format(args: FormatArgs) -> anyhow::Result { Ok(exit_code) } -async fn project_format( +async fn run_format( reporter: &Reporter, - project_path: &Path, + projects: &Projects, + project_hash: ProjectHash, + project_name: &String, check: bool, ) -> Result { - let brioche = brioche_core::BriocheBuilder::new(reporter.clone()) - .build() - .await?; - let projects = brioche_core::project::Projects::default(); - - let project_hash = projects.load(&brioche, project_path, true).await?; - let result = async { if check { - brioche_core::script::format::check_format(&projects, project_hash).await + brioche_core::script::format::check_format(projects, project_hash).await } else { - brioche_core::script::format::format(&projects, project_hash).await + brioche_core::script::format::format(projects, project_hash).await } } .instrument(tracing::info_span!("format")) @@ -89,8 +90,7 @@ async fn project_format( if !files.is_empty() { reporter.emit(superconsole::Lines::from_multiline_string( &format!( - "The following files of project '{project_path}' have been formatted:\n{files}", - project_path = project_path.display(), + "The following files of {project_name} have been formatted:\n{files}", files = files .iter() .map(|file| format!("- {}", file.display())) @@ -104,10 +104,7 @@ async fn project_format( Ok(true) } else if files.is_empty() { reporter.emit(superconsole::Lines::from_multiline_string( - &format!( - "All files of project '{project_path}' are formatted", - project_path = project_path.display() - ), + &format!("All files of {project_name} are formatted",), superconsole::style::ContentStyle::default(), )); @@ -115,8 +112,7 @@ async fn project_format( } else { reporter.emit(superconsole::Lines::from_multiline_string( &format!( - "The following files of project '{project_path}' are not formatted:\n{files}", - project_path = project_path.display(), + "The following files of {project_name} are not formatted:\n{files}", files = files .iter() .map(|file| format!("- {}", file.display())) diff --git a/crates/brioche/src/main.rs b/crates/brioche/src/main.rs index be07d78..5397349 100644 --- a/crates/brioche/src/main.rs +++ b/crates/brioche/src/main.rs @@ -229,6 +229,18 @@ struct ProjectArgs { registry: Option, } +#[derive(Debug, clap::Args)] +#[group(required = false, multiple = false)] +struct MultipleProjectArgs { + /// The path of the project directory to build [default: .] + #[clap(short, long)] + project: Vec, + + /// The name of a registry project to build + #[clap(id = "registry", short, long)] + registry_project: Vec, +} + async fn load_project( brioche: &brioche_core::Brioche, projects: &brioche_core::project::Projects, @@ -253,3 +265,28 @@ async fn load_project( Ok(project_hash) } + +fn consolidate_result( + reporter: &brioche_core::reporter::Reporter, + project_name: &String, + result: Result, + error_result: &mut Option<()>, +) { + match result { + Err(err) => { + reporter.emit(superconsole::Lines::from_multiline_string( + &format!("Error occurred with {project_name}: {err}", err = err), + superconsole::style::ContentStyle { + foreground_color: Some(superconsole::style::Color::Red), + ..superconsole::style::ContentStyle::default() + }, + )); + + *error_result = Some(()); + } + Ok(false) => { + *error_result = Some(()); + } + _ => {} + } +}