Skip to content
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
35 changes: 30 additions & 5 deletions crates/brioche-core/src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,18 @@ impl Projects {
Ok(module_paths.collect())
}

pub fn project_module_paths_for_projects(
&self,
project_hashes: &HashSet<ProjectHash>,
) -> anyhow::Result<Vec<PathBuf>> {
let projects = self
.inner
.read()
.map_err(|_| anyhow::anyhow!("failed to acquire 'projects' lock"))?;
let module_paths = projects.project_module_paths_for_projects(project_hashes)?;
Ok(module_paths.collect())
}

pub fn project_module_specifiers(
&self,
project_hash: ProjectHash,
Expand All @@ -233,7 +245,7 @@ impl Projects {
.read()
.map_err(|_| anyhow::anyhow!("failed to acquire 'projects' lock"))?;
let module_specifiers = projects.project_module_specifiers_for_projects(project_hashes)?;
Ok(module_specifiers)
Ok(module_specifiers.collect())
}

pub fn find_containing_project(&self, path: &Path) -> anyhow::Result<Option<ProjectHash>> {
Expand Down Expand Up @@ -548,6 +560,18 @@ impl ProjectsInner {
Ok(super::script::specifier::BriocheModuleSpecifier::File { path })
}

pub fn project_module_paths_for_projects(
&self,
project_hashes: &HashSet<ProjectHash>,
) -> anyhow::Result<impl Iterator<Item = PathBuf>> {
let mut paths = Vec::with_capacity(project_hashes.len());
for project_hash in project_hashes {
paths.push(self.project_module_paths(*project_hash)?);
}

Ok(paths.into_iter().flatten())
}

pub fn project_module_paths(
&self,
project_hash: ProjectHash,
Expand All @@ -574,14 +598,15 @@ impl ProjectsInner {
pub fn project_module_specifiers_for_projects(
&self,
project_hashes: &HashSet<ProjectHash>,
) -> anyhow::Result<HashSet<super::script::specifier::BriocheModuleSpecifier>> {
let mut module_specifiers = HashSet::new();
) -> anyhow::Result<impl Iterator<Item = super::script::specifier::BriocheModuleSpecifier>>
{
let mut module_specifiers = Vec::with_capacity(project_hashes.len());
for project_hash in project_hashes {
let module_paths = self.project_module_specifiers(*project_hash)?;
module_specifiers.extend(module_paths);
module_specifiers.push(module_paths);
}

Ok(module_specifiers)
Ok(module_specifiers.into_iter().flatten())
}

pub fn project_module_specifiers(
Expand Down
21 changes: 11 additions & 10 deletions crates/brioche-core/src/script/format.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,41 @@
use std::collections::HashSet;
use std::path::PathBuf;

use crate::project::{ProjectHash, Projects};

/// Formats the specified project using the provided formatter.
/// Formats the specified projects using the provided formatter.
///
/// This function takes a reference to the `Projects` struct, a `ProjectHash` representing the project to format.
/// This function takes a reference to the `Projects` struct, a list of `ProjectHash` representing the projects to format.
/// It returns a `Result` containing a vector of `PathBuf` representing the paths of the formatted files,
/// or an `anyhow::Error` if an error occurs.
pub async fn format(
projects: &Projects,
project_hash: ProjectHash,
project_hashes: &HashSet<ProjectHash>,
) -> anyhow::Result<Vec<PathBuf>> {
format_project(projects, project_hash, false).await
format_project(projects, project_hashes, false).await
}

/// Checks the formatting of the specified project using the provided formatter.
/// Checks the formatting of the specified projects using the provided formatter.
///
/// This function takes a reference to the `Projects` struct, a `ProjectHash` representing the project to check.
/// This function takes a reference to the `Projects` struct, a list of `ProjectHash` representing the projects to check.
/// It returns a `Result` containing a vector of `PathBuf` representing the paths of the unformatted files,
/// or an `anyhow::Error` if an error occurs.
pub async fn check_format(
projects: &Projects,
project_hash: ProjectHash,
project_hashes: &HashSet<ProjectHash>,
) -> anyhow::Result<Vec<PathBuf>> {
format_project(projects, project_hash, true).await
format_project(projects, project_hashes, true).await
}

#[tracing::instrument(skip(projects), err)]
async fn format_project(
projects: &Projects,
project_hash: ProjectHash,
project_hashes: &HashSet<ProjectHash>,
check: bool,
) -> anyhow::Result<Vec<PathBuf>> {
let mut result = vec![];

let module_paths = projects.project_module_paths(project_hash)?;
let module_paths = projects.project_module_paths_for_projects(project_hashes)?;
for path in module_paths {
let contents = tokio::fs::read_to_string(&path).await?;

Expand Down
5 changes: 3 additions & 2 deletions crates/brioche/src/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@ pub async fn check(
args.project.project
};

let mut project_names = HashMap::new();
let mut projects_to_check = HashSet::new();
// Pre-allocate capacity for project names and projects to check
let mut project_names = HashMap::with_capacity(project_paths.len());
let mut projects_to_check = HashSet::with_capacity(project_paths.len());

// Load each path project
for project_path in project_paths {
Expand Down
114 changes: 75 additions & 39 deletions crates/brioche/src/format.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashMap;
use std::collections::HashSet;
use std::{path::PathBuf, process::ExitCode};

use brioche_core::{
Expand Down Expand Up @@ -38,36 +40,56 @@ pub async fn format(args: FormatArgs) -> anyhow::Result<ExitCode> {

let mut error_result = Option::None;

// Loop over the projects
// Pre-allocate capacity for project names and projects to format
let mut project_names = HashMap::with_capacity(args.project.len());
let mut projects_to_format = HashSet::with_capacity(args.project.len());

// Load each path project
for project_path in args.project {
let project_name = format!("project '{name}'", name = project_path.display());

match projects
let project_hash = projects
.load(
&brioche,
&project_path,
ProjectValidation::Standard,
ProjectLocking::Unlocked,
)
.await
{
Ok(project_hash) => {
let result = run_format(
.await;

let project_hash = match project_hash {
Ok(project_hash) => project_hash,
Err(error) => {
consolidate_result(
&reporter,
&projects,
project_hash,
&project_name,
args.check,
)
.await;
consolidate_result(&reporter, Some(&project_name), result, &mut error_result);
Some(&project_name),
Err(error),
&mut error_result,
);
continue;
}
Err(e) => {
consolidate_result(&reporter, Some(&project_name), Err(e), &mut error_result);
}
}
};

project_names.entry(project_hash).or_insert(project_name);
projects_to_format.insert(project_hash);
}

let project_name = if project_names.len() == 1 {
Some(project_names.values().next().unwrap())
} else {
None
};
let project_name = project_name.map(String::as_str);

let result = run_format(
&reporter,
&projects,
&projects_to_format,
project_name,
args.check,
)
.await;
consolidate_result(&reporter, project_name, result, &mut error_result);

guard.shutdown_console().await;
brioche.wait_for_tasks().await;

Expand All @@ -79,15 +101,15 @@ pub async fn format(args: FormatArgs) -> anyhow::Result<ExitCode> {
async fn run_format(
reporter: &Reporter,
projects: &Projects,
project_hash: ProjectHash,
project_name: &String,
project_hashes: &HashSet<ProjectHash>,
project_name: Option<&str>,
check: bool,
) -> Result<bool, anyhow::Error> {
let result = async {
if check {
brioche_core::script::format::check_format(projects, project_hash).await
brioche_core::script::format::check_format(projects, project_hashes).await
} else {
brioche_core::script::format::format(projects, project_hash).await
brioche_core::script::format::format(projects, project_hashes).await
}
}
.instrument(tracing::info_span!("format"))
Expand All @@ -100,37 +122,51 @@ async fn run_format(

if !check {
if !files.is_empty() {
let files = files
.iter()
.map(|file| format!("- {}", file.display()))
.collect::<Vec<_>>()
.join("\n");

let format_string = project_name.map_or_else(|| format!("The following files in projects have been formatted:\n{files}"), |project_name| format!(
"The following files of {project_name} have been formatted:\n{files}"
));

reporter.emit(superconsole::Lines::from_multiline_string(
&format!(
"The following files of {project_name} have been formatted:\n{files}",
files = files
.iter()
.map(|file| format!("- {}", file.display()))
.collect::<Vec<_>>()
.join("\n")
),
&format_string,
superconsole::style::ContentStyle::default(),
));
}

Ok(true)
} else if files.is_empty() {
let format_string = project_name.map_or_else(
|| "All files in projects are formatted".to_string(),
|project_name| format!("All files of {project_name} are formatted"),
);

reporter.emit(superconsole::Lines::from_multiline_string(
&format!("All files of {project_name} are formatted"),
&format_string,
superconsole::style::ContentStyle::default(),
));

Ok(true)
} else {
let files = files
.iter()
.map(|file| format!("- {}", file.display()))
.collect::<Vec<_>>()
.join("\n");

let format_string = project_name.map_or_else(
|| format!("The following files in projects are not formatted:\n{files}"),
|project_name| {
format!("The following files of {project_name} are not formatted:\n{files}")
},
);

reporter.emit(superconsole::Lines::from_multiline_string(
&format!(
"The following files of {project_name} are not formatted:\n{files}",
files = files
.iter()
.map(|file| format!("- {}", file.display()))
.collect::<Vec<_>>()
.join("\n")
),
&format_string,
superconsole::style::ContentStyle::default(),
));

Expand Down