diff --git a/Cargo.lock b/Cargo.lock index 76a6e94..3ca4b82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aho-corasick" @@ -651,6 +651,7 @@ dependencies = [ "log", "once_cell", "rayon", + "serde", "serde_json", "teamsearch_matcher", "teamsearch_utils", diff --git a/crates/teamsearch/Cargo.toml b/crates/teamsearch/Cargo.toml index 3603461..39ae301 100644 --- a/crates/teamsearch/Cargo.toml +++ b/crates/teamsearch/Cargo.toml @@ -20,4 +20,5 @@ log = { workspace = true } once_cell = { workspace = true } rayon = { workspace = true } wild = { workspace = true} +serde = { workspace = true } serde_json = { workspace = true } diff --git a/crates/teamsearch/src/cli.rs b/crates/teamsearch/src/cli.rs index a819172..3c092d2 100644 --- a/crates/teamsearch/src/cli.rs +++ b/crates/teamsearch/src/cli.rs @@ -84,7 +84,7 @@ pub struct FindCommand { /// } /// ] /// ``` - #[clap(long, help = "Display the results using a JSON format")] + #[clap(long, help = "Display the results using in JSON format")] pub json: bool, } @@ -101,4 +101,22 @@ pub struct LookupCommand { /// Specify the path of the file of the codeowners. #[clap(long, short, help = "Specify the path of the CODEOWNERS file [default: CODEOWNERS]")] pub codeowners: PathBuf, + + /// Display the results using a JSON format. We output the contents + /// of the search in the following format: + /// + /// ```json + /// [ + /// { + /// "path": "some/foo/result.rs", + /// "team": "@some-team" + /// }, + /// { + /// "path": "some/bar/result.rs", + /// "team": null + /// }, + /// ] + /// ``` + #[clap(long, help = "Display the results using in JSON format")] + pub json: bool, } diff --git a/crates/teamsearch/src/commands/find.rs b/crates/teamsearch/src/commands/find.rs index 3bb2546..6937b4a 100644 --- a/crates/teamsearch/src/commands/find.rs +++ b/crates/teamsearch/src/commands/find.rs @@ -15,13 +15,14 @@ use teamsearch_workspace::{ settings::{FilePattern, Settings}, }; +/// The result of a search. #[derive(Default, Constructor)] -pub struct FindResult { +pub(crate) struct FindResult { /// The items that we're found within the files. pub file_matches: Vec, } -pub fn find( +pub(crate) fn find( files: &[PathBuf], mut settings: Settings, team: Vec, diff --git a/crates/teamsearch/src/commands/lookup.rs b/crates/teamsearch/src/commands/lookup.rs index c020ab4..c477025 100644 --- a/crates/teamsearch/src/commands/lookup.rs +++ b/crates/teamsearch/src/commands/lookup.rs @@ -2,12 +2,31 @@ use std::{iter::once, path::PathBuf}; use anyhow::Result; use itertools::Itertools; +use serde::Serialize; use teamsearch_utils::fs; use teamsearch_workspace::{codeowners::CodeOwners, settings::Settings}; -pub fn lookup(files: &[PathBuf], settings: Settings) -> Result<()> { +/// An lookup entry, representing a file and its corresponding +/// owners. +#[derive(Serialize)] +pub(crate) struct LookupEntry { + /// The owner of the file, if any. + pub(crate) team: Option, + + /// The path of the entry. + pub(crate) path: PathBuf, +} + +/// The result of an owner lookup. +#[derive(Serialize, Default)] +#[serde(transparent)] +pub(crate) struct LookupResult { + pub(crate) entries: Vec, +} + +pub fn lookup(files: &[PathBuf], settings: Settings) -> Result { if files.is_empty() { - return Ok(()); + return Ok(LookupResult::default()); } // Compute the "root" of all of the paths including the provided paths and the @@ -20,11 +39,12 @@ pub fn lookup(files: &[PathBuf], settings: Settings) -> Result<()> { // extract the given patterns that are specified for the particular team. let codeowners = CodeOwners::parse_from_file(&settings.codeowners, &root)?; + let mut entries = Vec::new(); + // For each path (other than last), we need to find the team that owns it. for path in paths.iter().take(paths.len() - 1) { - let team = codeowners.lookup(path).unwrap_or("none"); - println!("{}: {}", path.display(), team); + entries.push(LookupEntry { path: path.clone(), team: codeowners.lookup(path) }); } - Ok(()) + Ok(LookupResult { entries }) } diff --git a/crates/teamsearch/src/lib.rs b/crates/teamsearch/src/lib.rs index fa03d07..d234d1e 100644 --- a/crates/teamsearch/src/lib.rs +++ b/crates/teamsearch/src/lib.rs @@ -16,7 +16,7 @@ use std::{ use annotate_snippets::{Level, Renderer, Snippet}; use anyhow::{Ok, Result, anyhow}; use cli::{FindCommand, LookupCommand}; -use commands::find::FindResult; +use commands::{find::FindResult, lookup::LookupEntry}; use crash::crash_handler; use log::info; use teamsearch_utils::{logging::ToolLogger, stream::CompilerOutputStream}; @@ -146,7 +146,16 @@ fn lookup(args: LookupCommand) -> Result { } let settings = Settings::new(true, args.codeowners); - commands::lookup::lookup(&files, settings)?; + let results = commands::lookup::lookup(&files, settings)?; + + if args.json { + // Print out the results in JSON format. + println!("{}", serde_json::to_string_pretty(&results)?); + } else { + for LookupEntry { path, team } in results.entries { + info!("{}: {}", path.display(), team.as_ref().map_or("none", |t| t.as_str())) + } + } Ok(ExitStatus::Success) } diff --git a/crates/teamsearch_workspace/src/codeowners.rs b/crates/teamsearch_workspace/src/codeowners.rs index 7fdd69b..61909f2 100644 --- a/crates/teamsearch_workspace/src/codeowners.rs +++ b/crates/teamsearch_workspace/src/codeowners.rs @@ -30,7 +30,7 @@ impl CodeOwners { } /// Lookup a file path to see which team owns it. - pub fn lookup(&self, path: &PathBuf) -> Option<&str> { + pub fn lookup(&self, path: &PathBuf) -> Option { let path = fs::normalize_path(path); // @@Hack: Check if we're missing a `/` at the end of the path. @@ -46,7 +46,7 @@ impl CodeOwners { let set = self.get_pattern_for_team(owner); if set.is_match(&path_pat) { - return Some(owner); + return Some(owner.to_string()); } }