@@ -46,6 +46,7 @@ pub fn codeowners_parse(
4646 dbg ! ( cache) ;
4747
4848 println ! ( "CODEOWNERS parsing completed successfully" ) ;
49+
4950 Ok ( ( ) )
5051}
5152
@@ -61,22 +62,273 @@ pub fn codeowners_list_files(
6162 info ! ( "Unowned only: {}" , unowned) ;
6263 info ! ( "Output format: {}" , format) ;
6364
64- println ! ( "Files listing completed" ) ;
65+ // Determine the cache file path based on repository path
66+ let repo_path = path. unwrap_or_else ( || std:: path:: Path :: new ( "." ) ) ;
67+ //let config = utils::app_config::AppConfig::fetch()?;
68+ // let cache_dir = config
69+ // .cache_dir
70+ // .unwrap_or_else(|| repo_path.join(".codeowners.cache"));
71+ let cache_file = repo_path. join ( ".codeowners.cache" ) ;
72+
73+ if !cache_file. exists ( ) {
74+ return Err ( utils:: error:: Error :: new ( & format ! (
75+ "Cache file not found at {}. Please run 'codeowners parse' first." ,
76+ cache_file. display( )
77+ ) ) ) ;
78+ }
79+
80+ // Load the cache
81+ let cache = load_cache ( & cache_file) ?;
82+
83+ // Filter files based on criteria
84+ let filtered_files = cache
85+ . files
86+ . iter ( )
87+ . filter ( |file| {
88+ // Check if we should include this file based on filters
89+ let passes_owner_filter = match owners {
90+ Some ( owner_filter) => {
91+ let owner_patterns: Vec < & str > = owner_filter. split ( ',' ) . collect ( ) ;
92+ file. owners . iter ( ) . any ( |owner| {
93+ owner_patterns
94+ . iter ( )
95+ . any ( |pattern| owner. identifier . contains ( pattern) )
96+ } )
97+ }
98+ None => true ,
99+ } ;
100+
101+ let passes_tag_filter = match tags {
102+ Some ( tag_filter) => {
103+ let tag_patterns: Vec < & str > = tag_filter. split ( ',' ) . collect ( ) ;
104+ file. tags
105+ . iter ( )
106+ . any ( |tag| tag_patterns. iter ( ) . any ( |pattern| tag. 0 . contains ( pattern) ) )
107+ }
108+ None => true ,
109+ } ;
110+
111+ let passes_unowned_filter = if unowned {
112+ file. owners . is_empty ( )
113+ } else {
114+ true
115+ } ;
116+
117+ passes_owner_filter && passes_tag_filter && passes_unowned_filter
118+ } )
119+ . collect :: < Vec < _ > > ( ) ;
120+
121+ // Output the filtered files in the requested format
122+ match format {
123+ OutputFormat :: Text => {
124+ for file in filtered_files {
125+ let owners_str = file
126+ . owners
127+ . iter ( )
128+ . map ( |o| o. identifier . clone ( ) )
129+ . collect :: < Vec < _ > > ( )
130+ . join ( ", " ) ;
131+
132+ let tags_str = file
133+ . tags
134+ . iter ( )
135+ . map ( |t| t. 0 . clone ( ) )
136+ . collect :: < Vec < _ > > ( )
137+ . join ( ", " ) ;
138+
139+ println ! ( "File: {}" , file. path. display( ) ) ;
140+ println ! (
141+ " Owners: {}" ,
142+ if owners_str. is_empty( ) {
143+ "None"
144+ } else {
145+ & owners_str
146+ }
147+ ) ;
148+ println ! (
149+ " Tags: {}" ,
150+ if tags_str. is_empty( ) {
151+ "None"
152+ } else {
153+ & tags_str
154+ }
155+ ) ;
156+ println ! ( ) ;
157+ }
158+ }
159+ OutputFormat :: Json => {
160+ println ! ( "{}" , serde_json:: to_string_pretty( & filtered_files) . unwrap( ) ) ;
161+ }
162+ OutputFormat :: Bincode => {
163+ eprintln ! ( "Binary output to stdout not supported for file listing" ) ;
164+ return Err ( utils:: error:: Error :: new (
165+ "Binary output to stdout not supported" ,
166+ ) ) ;
167+ }
168+ }
169+
65170 Ok ( ( ) )
66171}
67172
68173/// Display aggregated owner statistics and associations
69- pub fn codeowners_list_owners ( format : & OutputFormat ) -> Result < ( ) > {
174+ pub fn codeowners_list_owners ( path : Option < & std:: path:: Path > , format : & OutputFormat ) -> Result < ( ) > {
175+ info ! ( "Listing owners" ) ;
70176 info ! ( "Output format: {}" , format) ;
71177
72- println ! ( "Owners listing completed" ) ;
178+ // Determine the cache file path based on repository path
179+ let repo_path = path. unwrap_or_else ( || std:: path:: Path :: new ( "." ) ) ;
180+ let cache_file = repo_path. join ( ".codeowners.cache" ) ;
181+
182+ if !cache_file. exists ( ) {
183+ return Err ( utils:: error:: Error :: new ( & format ! (
184+ "Cache file not found at {}. Please run 'codeowners parse' first." ,
185+ cache_file. display( )
186+ ) ) ) ;
187+ }
188+
189+ // Load the cache
190+ let cache = load_cache ( & cache_file) ?;
191+
192+ // Process the owners from the cache
193+ match format {
194+ OutputFormat :: Text => {
195+ println ! ( "CODEOWNERS Ownership Report" ) ;
196+ println ! ( "==========================\n " ) ;
197+
198+ if cache. owners_map . is_empty ( ) {
199+ println ! ( "No owners found in the codebase." ) ;
200+ } else {
201+ // Sort owners by number of files they own (descending)
202+ let mut owners_with_counts: Vec < _ > = cache. owners_map . iter ( ) . collect ( ) ;
203+ owners_with_counts. sort_by ( |a, b| b. 1 . len ( ) . cmp ( & a. 1 . len ( ) ) ) ;
204+
205+ for ( owner, paths) in owners_with_counts {
206+ println ! ( "Owner: {} ({})" , owner. identifier, owner. owner_type) ;
207+ println ! ( "Files owned: {}" , paths. len( ) ) ;
208+
209+ // List first 5 files (to avoid overwhelming output)
210+ if !paths. is_empty ( ) {
211+ println ! ( "Sample files:" ) ;
212+ for path in paths. iter ( ) . take ( 5 ) {
213+ println ! ( " - {}" , path. display( ) ) ;
214+ }
215+
216+ if paths. len ( ) > 5 {
217+ println ! ( " ... and {} more" , paths. len( ) - 5 ) ;
218+ }
219+ }
220+
221+ println ! ( ) ; // Empty line between owners
222+ }
223+ }
224+ }
225+ OutputFormat :: Json => {
226+ // Convert to a more friendly JSON structure
227+ let owners_data: Vec < _ > = cache. owners_map . iter ( )
228+ . map ( |( owner, paths) | {
229+ serde_json:: json!( {
230+ "identifier" : owner. identifier,
231+ "type" : format!( "{:?}" , owner. owner_type) ,
232+ "file_count" : paths. len( ) ,
233+ "files" : paths. iter( ) . map( |p| p. to_string_lossy( ) . to_string( ) ) . collect:: <Vec <_>>( )
234+ } )
235+ } )
236+ . collect ( ) ;
237+
238+ println ! ( "{}" , serde_json:: to_string_pretty( & owners_data) . unwrap( ) ) ;
239+ }
240+ OutputFormat :: Bincode => {
241+ eprintln ! ( "Binary output to stdout not supported for owners listing" ) ;
242+ return Err ( utils:: error:: Error :: new (
243+ "Binary output to stdout not supported" ,
244+ ) ) ;
245+ }
246+ }
247+
248+ println ! (
249+ "Owners listing completed - {} owners found" ,
250+ cache. owners_map. len( )
251+ ) ;
73252 Ok ( ( ) )
74253}
75254
76255/// Audit and analyze tag usage across CODEOWNERS files
77- pub fn codeowners_list_tags ( format : & OutputFormat ) -> Result < ( ) > {
256+ pub fn codeowners_list_tags ( path : Option < & std:: path:: Path > , format : & OutputFormat ) -> Result < ( ) > {
257+ info ! ( "Listing tags" ) ;
78258 info ! ( "Output format: {}" , format) ;
79259
80- println ! ( "Tags listing completed" ) ;
260+ // Determine the cache file path based on repository path
261+ let repo_path = path. unwrap_or_else ( || std:: path:: Path :: new ( "." ) ) ;
262+ let cache_file = repo_path. join ( ".codeowners.cache" ) ;
263+
264+ if !cache_file. exists ( ) {
265+ return Err ( utils:: error:: Error :: new ( & format ! (
266+ "Cache file not found at {}. Please run 'codeowners parse' first." ,
267+ cache_file. display( )
268+ ) ) ) ;
269+ }
270+
271+ // Load the cache
272+ let cache = load_cache ( & cache_file) ?;
273+
274+ // Process the tags from the cache
275+ match format {
276+ OutputFormat :: Text => {
277+ println ! ( "CODEOWNERS Tags Report" ) ;
278+ println ! ( "======================\n " ) ;
279+
280+ if cache. tags_map . is_empty ( ) {
281+ println ! ( "No tags found in the codebase." ) ;
282+ } else {
283+ // Sort tags by number of files they're associated with (descending)
284+ let mut tags_with_counts: Vec < _ > = cache. tags_map . iter ( ) . collect ( ) ;
285+ tags_with_counts. sort_by ( |a, b| b. 1 . len ( ) . cmp ( & a. 1 . len ( ) ) ) ;
286+
287+ for ( tag, paths) in tags_with_counts {
288+ println ! ( "Tag: {}" , tag. 0 ) ;
289+ println ! ( "Files tagged: {}" , paths. len( ) ) ;
290+
291+ // List first 5 files (to avoid overwhelming output)
292+ if !paths. is_empty ( ) {
293+ println ! ( "Sample files:" ) ;
294+ for path in paths. iter ( ) . take ( 5 ) {
295+ println ! ( " - {}" , path. display( ) ) ;
296+ }
297+
298+ if paths. len ( ) > 5 {
299+ println ! ( " ... and {} more" , paths. len( ) - 5 ) ;
300+ }
301+ }
302+
303+ println ! ( ) ; // Empty line between tags
304+ }
305+ }
306+ }
307+ OutputFormat :: Json => {
308+ // Convert to a more friendly JSON structure
309+ let tags_data: Vec < _ > = cache. tags_map . iter ( )
310+ . map ( |( tag, paths) | {
311+ serde_json:: json!( {
312+ "name" : tag. 0 ,
313+ "file_count" : paths. len( ) ,
314+ "files" : paths. iter( ) . map( |p| p. to_string_lossy( ) . to_string( ) ) . collect:: <Vec <_>>( )
315+ } )
316+ } )
317+ . collect ( ) ;
318+
319+ println ! ( "{}" , serde_json:: to_string_pretty( & tags_data) . unwrap( ) ) ;
320+ }
321+ OutputFormat :: Bincode => {
322+ eprintln ! ( "Binary output to stdout not supported for tags listing" ) ;
323+ return Err ( utils:: error:: Error :: new (
324+ "Binary output to stdout not supported" ,
325+ ) ) ;
326+ }
327+ }
328+
329+ println ! (
330+ "Tags listing completed - {} tags found" ,
331+ cache. tags_map. len( )
332+ ) ;
81333 Ok ( ( ) )
82334}
0 commit comments