1- use core:: fmt;
2- use std:: {
3- collections:: HashMap ,
4- fs:: File ,
5- path:: { Path , PathBuf } ,
6- process:: Command ,
7- } ;
1+ use std:: { path:: Path , process:: Command } ;
82
9- use error_stack:: { Context , Result , ResultExt } ;
10- use serde:: { Deserialize , Serialize } ;
3+ use error_stack:: { Result , ResultExt } ;
114
125use crate :: {
136 cache:: { Cache , Caching , file:: GlobalCache , noop:: NoopCache } ,
147 config:: Config ,
158 ownership:: { FileOwner , Ownership } ,
16- project:: Team ,
179 project_builder:: ProjectBuilder ,
1810} ;
1911
20- #[ derive( Debug , Default , Serialize , Deserialize ) ]
21- pub struct RunResult {
22- pub validation_errors : Vec < String > ,
23- pub io_errors : Vec < String > ,
24- pub info_messages : Vec < String > ,
25- }
26- #[ derive( Debug , Clone ) ]
27- pub struct RunConfig {
28- pub project_root : PathBuf ,
29- pub codeowners_file_path : PathBuf ,
30- pub config_path : PathBuf ,
31- pub no_cache : bool ,
32- }
12+ mod types;
13+ pub use self :: types:: { Error , RunConfig , RunResult } ;
14+ mod api;
15+ pub use self :: api:: * ;
3316
3417pub struct Runner {
3518 run_config : RunConfig ,
3619 ownership : Ownership ,
3720 cache : Cache ,
38- }
39-
40- pub fn for_file ( run_config : & RunConfig , file_path : & str , from_codeowners : bool ) -> RunResult {
41- if from_codeowners {
42- return for_file_codeowners_only ( run_config, file_path) ;
43- }
44- for_file_optimized ( run_config, file_path)
45- }
46-
47- pub fn file_owner_for_file ( run_config : & RunConfig , file_path : & str ) -> Result < Option < FileOwner > , Error > {
48- let config = config_from_path ( & run_config. config_path ) ?;
49- use crate :: ownership:: for_file_fast:: find_file_owners;
50- let owners = find_file_owners ( & run_config. project_root , & config, std:: path:: Path :: new ( file_path) ) . map_err ( Error :: Io ) ?;
51- Ok ( owners. first ( ) . cloned ( ) )
52- }
53-
54- pub fn team_for_file ( run_config : & RunConfig , file_path : & str ) -> Result < Option < Team > , Error > {
55- let owner = file_owner_for_file ( run_config, file_path) ?;
56- Ok ( owner. map ( |fo| fo. team . clone ( ) ) )
21+ config : Config ,
5722}
5823
5924pub fn version ( ) -> String {
6025 env ! ( "CARGO_PKG_VERSION" ) . to_string ( )
6126}
6227
63- pub fn for_team ( run_config : & RunConfig , team_name : & str ) -> RunResult {
64- run_with_runner ( run_config, |runner| runner. for_team ( team_name) )
65- }
66-
67- pub fn validate ( run_config : & RunConfig , _file_paths : Vec < String > ) -> RunResult {
68- run_with_runner ( run_config, |runner| runner. validate ( ) )
69- }
70-
71- pub fn generate ( run_config : & RunConfig , git_stage : bool ) -> RunResult {
72- run_with_runner ( run_config, |runner| runner. generate ( git_stage) )
73- }
74-
75- pub fn generate_and_validate ( run_config : & RunConfig , _file_paths : Vec < String > , git_stage : bool ) -> RunResult {
76- run_with_runner ( run_config, |runner| runner. generate_and_validate ( git_stage) )
77- }
78-
79- pub fn delete_cache ( run_config : & RunConfig ) -> RunResult {
80- run_with_runner ( run_config, |runner| runner. delete_cache ( ) )
81- }
82-
83- pub fn crosscheck_owners ( run_config : & RunConfig ) -> RunResult {
84- run_with_runner ( run_config, |runner| runner. crosscheck_owners ( ) )
85- }
86-
8728pub type Runnable = fn ( Runner ) -> RunResult ;
8829
89- pub fn run_with_runner < F > ( run_config : & RunConfig , runnable : F ) -> RunResult
30+ pub fn run < F > ( run_config : & RunConfig , runnable : F ) -> RunResult
9031where
9132 F : FnOnce ( Runner ) -> RunResult ,
9233{
@@ -102,35 +43,12 @@ where
10243 runnable ( runner)
10344}
10445
105- impl RunResult {
106- pub fn has_errors ( & self ) -> bool {
107- !self . validation_errors . is_empty ( ) || !self . io_errors . is_empty ( )
108- }
109- }
110-
111- #[ derive( Debug ) ]
112- pub enum Error {
113- Io ( String ) ,
114- ValidationFailed ,
115- }
116-
117- impl Context for Error { }
118- impl fmt:: Display for Error {
119- fn fmt ( & self , fmt : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
120- match self {
121- Error :: Io ( msg) => fmt. write_str ( msg) ,
122- Error :: ValidationFailed => fmt. write_str ( "Error::ValidationFailed" ) ,
123- }
46+ pub ( crate ) fn config_from_path ( path : & Path ) -> Result < Config , Error > {
47+ match crate :: config:: Config :: load_from_path ( path) {
48+ Ok ( c) => Ok ( c) ,
49+ Err ( msg) => Err ( error_stack:: Report :: new ( Error :: Io ( msg) ) ) ,
12450 }
12551}
126-
127- pub ( crate ) fn config_from_path ( path : & PathBuf ) -> Result < Config , Error > {
128- let config_file = File :: open ( path)
129- . change_context ( Error :: Io ( format ! ( "Can't open config file: {}" , & path. to_string_lossy( ) ) ) )
130- . attach_printable ( format ! ( "Can't open config file: {}" , & path. to_string_lossy( ) ) ) ?;
131-
132- serde_yaml:: from_reader ( config_file) . change_context ( Error :: Io ( format ! ( "Can't parse config file: {}" , & path. to_string_lossy( ) ) ) )
133- }
13452impl Runner {
13553 pub fn new ( run_config : & RunConfig ) -> Result < Self , Error > {
13654 let config = config_from_path ( & run_config. config_path ) ?;
@@ -168,6 +86,7 @@ impl Runner {
16886 run_config : run_config. clone ( ) ,
16987 ownership,
17088 cache,
89+ config,
17190 } )
17291 }
17392
@@ -255,111 +174,75 @@ impl Runner {
255174 pub fn crosscheck_owners ( & self ) -> RunResult {
256175 crate :: crosscheck:: crosscheck_owners ( & self . run_config , & self . cache )
257176 }
258- }
259177
260- fn for_file_codeowners_only ( run_config : & RunConfig , file_path : & str ) -> RunResult {
261- match team_for_file_from_codeowners ( run_config, file_path) {
262- Ok ( Some ( team) ) => {
263- let relative_team_path = team
264- . path
265- . strip_prefix ( & run_config. project_root )
266- . unwrap_or ( team. path . as_path ( ) )
267- . to_string_lossy ( )
268- . to_string ( ) ;
269- RunResult {
270- info_messages : vec ! [ format!(
271- "Team: {}\n Github Team: {}\n Team YML: {}\n Description:\n - Owner inferred from codeowners file" ,
272- team. name, team. github_team, relative_team_path
273- ) ] ,
274- ..Default :: default ( )
275- }
276- }
277- Ok ( None ) => RunResult :: default ( ) ,
278- Err ( err) => RunResult {
279- io_errors : vec ! [ err. to_string( ) ] ,
280- ..Default :: default ( )
281- } ,
178+ pub fn owners_for_file ( & self , file_path : & str ) -> Result < Vec < FileOwner > , Error > {
179+ use crate :: ownership:: for_file_fast:: find_file_owners;
180+ let owners = find_file_owners ( & self . run_config . project_root , & self . config , std:: path:: Path :: new ( file_path) ) . map_err ( Error :: Io ) ?;
181+ Ok ( owners)
282182 }
283- }
284183
285- // For an array of file paths, return a map of file path to its owning team
286- pub fn teams_for_files_from_codeowners ( run_config : & RunConfig , file_paths : & [ String ] ) -> Result < HashMap < String , Option < Team > > , Error > {
287- let relative_file_paths: Vec < PathBuf > = file_paths
288- . iter ( )
289- . map ( |path| Path :: new ( path) . strip_prefix ( & run_config. project_root ) . unwrap_or ( Path :: new ( path) ) )
290- . map ( |path| path. to_path_buf ( ) )
291- . collect ( ) ;
292-
293- let parser = build_codeowners_parser ( run_config) ?;
294- Ok ( parser
295- . teams_from_files_paths ( & relative_file_paths)
296- . map_err ( |e| Error :: Io ( e. to_string ( ) ) ) ?)
297- }
298-
299- fn build_codeowners_parser ( run_config : & RunConfig ) -> Result < crate :: ownership:: codeowners_file_parser:: Parser , Error > {
300- let config = config_from_path ( & run_config. config_path ) ?;
301- Ok ( crate :: ownership:: codeowners_file_parser:: Parser {
302- codeowners_file_path : run_config. codeowners_file_path . clone ( ) ,
303- project_root : run_config. project_root . clone ( ) ,
304- team_file_globs : config. team_file_glob . clone ( ) ,
305- } )
306- }
307-
308- pub fn team_for_file_from_codeowners ( run_config : & RunConfig , file_path : & str ) -> Result < Option < Team > , Error > {
309- let relative_file_path = Path :: new ( file_path)
310- . strip_prefix ( & run_config. project_root )
311- . unwrap_or ( Path :: new ( file_path) ) ;
312-
313- let parser = build_codeowners_parser ( run_config) ?;
314- Ok ( parser
315- . team_from_file_path ( Path :: new ( relative_file_path) )
316- . map_err ( |e| Error :: Io ( e. to_string ( ) ) ) ?)
317- }
318-
319- fn for_file_optimized ( run_config : & RunConfig , file_path : & str ) -> RunResult {
320- let config = match config_from_path ( & run_config. config_path ) {
321- Ok ( c) => c,
322- Err ( err) => {
323- return RunResult {
324- io_errors : vec ! [ err. to_string( ) ] ,
325- ..Default :: default ( )
326- } ;
327- }
328- } ;
184+ pub fn for_file_optimized ( & self , file_path : & str ) -> RunResult {
185+ let file_owners = match self . owners_for_file ( file_path) {
186+ Ok ( v) => v,
187+ Err ( err) => {
188+ return RunResult {
189+ io_errors : vec ! [ err. to_string( ) ] ,
190+ ..Default :: default ( )
191+ } ;
192+ }
193+ } ;
329194
330- use crate :: ownership:: for_file_fast:: find_file_owners;
331- let file_owners = match find_file_owners ( & run_config. project_root , & config, std:: path:: Path :: new ( file_path) ) {
332- Ok ( v) => v,
333- Err ( err) => {
334- return RunResult {
335- io_errors : vec ! [ err] ,
336- ..Default :: default ( )
337- } ;
195+ let info_messages: Vec < String > = match file_owners. len ( ) {
196+ 0 => vec ! [ format!( "{}" , FileOwner :: default ( ) ) ] ,
197+ 1 => vec ! [ format!( "{}" , file_owners[ 0 ] ) ] ,
198+ _ => {
199+ let mut error_messages = vec ! [ "Error: file is owned by multiple teams!" . to_string( ) ] ;
200+ for file_owner in file_owners {
201+ error_messages. push ( format ! ( "\n {}" , file_owner) ) ;
202+ }
203+ return RunResult {
204+ validation_errors : error_messages,
205+ ..Default :: default ( )
206+ } ;
207+ }
208+ } ;
209+ RunResult {
210+ info_messages,
211+ ..Default :: default ( )
338212 }
339- } ;
213+ }
340214
341- let info_messages: Vec < String > = match file_owners. len ( ) {
342- 0 => vec ! [ format!( "{}" , FileOwner :: default ( ) ) ] ,
343- 1 => vec ! [ format!( "{}" , file_owners[ 0 ] ) ] ,
344- _ => {
345- let mut error_messages = vec ! [ "Error: file is owned by multiple teams!" . to_string( ) ] ;
346- for file_owner in file_owners {
347- error_messages. push ( format ! ( "\n {}" , file_owner) ) ;
215+ pub fn for_file_codeowners_only ( & self , file_path : & str ) -> RunResult {
216+ match team_for_file_from_codeowners ( & self . run_config , file_path) {
217+ Ok ( Some ( team) ) => {
218+ let relative_team_path = team
219+ . path
220+ . strip_prefix ( & self . run_config . project_root )
221+ . unwrap_or ( team. path . as_path ( ) )
222+ . to_string_lossy ( )
223+ . to_string ( ) ;
224+ RunResult {
225+ info_messages : vec ! [ format!(
226+ "Team: {}\n Github Team: {}\n Team YML: {}\n Description:\n - Owner inferred from codeowners file" ,
227+ team. name, team. github_team, relative_team_path
228+ ) ] ,
229+ ..Default :: default ( )
230+ }
348231 }
349- return RunResult {
350- validation_errors : error_messages,
232+ Ok ( None ) => RunResult :: default ( ) ,
233+ Err ( err) => RunResult {
234+ io_errors : vec ! [ err. to_string( ) ] ,
351235 ..Default :: default ( )
352- } ;
236+ } ,
353237 }
354- } ;
355- RunResult {
356- info_messages,
357- ..Default :: default ( )
358238 }
359239}
360240
241+ // removed free functions for for_file_* variants in favor of Runner methods
242+
361243#[ cfg( test) ]
362244mod tests {
245+ use std:: path:: Path ;
363246 use tempfile:: tempdir;
364247
365248 use super :: * ;
0 commit comments