1+ //! File analysis for multi-step generation.
2+
3+ use anyhow:: Result ;
4+ use serde:: { Deserialize , Serialize } ;
5+ use async_openai:: config:: OpenAIConfig ;
6+ use async_openai:: Client ;
7+ use serde_json:: Value ;
8+
9+ /// Represents a parsed file from the git diff
10+ #[ derive( Debug ) ]
11+ pub struct ParsedFile {
12+ pub path : String ,
13+ pub operation : String ,
14+ pub diff_content : String ,
15+ }
16+
17+ #[ derive( Debug , Clone , Serialize , Deserialize ) ]
18+ pub struct FileAnalysis {
19+ pub lines_added : u32 ,
20+ pub lines_removed : u32 ,
21+ pub category : FileCategory ,
22+ pub summary : String ,
23+ }
24+
25+ #[ derive( Debug , Clone , Copy , Serialize , Deserialize , PartialEq , Eq ) ]
26+ pub enum FileCategory {
27+ Source ,
28+ Test ,
29+ Config ,
30+ Docs ,
31+ Binary ,
32+ Build ,
33+ }
34+
35+ impl FileCategory {
36+ pub fn as_str ( & self ) -> & ' static str {
37+ match self {
38+ FileCategory :: Source => "source" ,
39+ FileCategory :: Test => "test" ,
40+ FileCategory :: Config => "config" ,
41+ FileCategory :: Docs => "docs" ,
42+ FileCategory :: Binary => "binary" ,
43+ FileCategory :: Build => "build" ,
44+ }
45+ }
46+ }
47+
48+ impl From < & str > for FileCategory {
49+ fn from ( s : & str ) -> Self {
50+ match s {
51+ "source" => FileCategory :: Source ,
52+ "test" => FileCategory :: Test ,
53+ "config" => FileCategory :: Config ,
54+ "docs" => FileCategory :: Docs ,
55+ "binary" => FileCategory :: Binary ,
56+ "build" => FileCategory :: Build ,
57+ _ => FileCategory :: Source , // default fallback
58+ }
59+ }
60+ }
61+
62+ /// Analyze a file locally without API
63+ pub fn analyze_file (
64+ path : & str ,
65+ diff_content : & str ,
66+ operation : & str ,
67+ ) -> FileAnalysis {
68+ // This will be moved from multi_step_analysis.rs analyze_file function
69+ crate :: multi_step_analysis:: analyze_file ( path, diff_content, operation) . into ( )
70+ }
71+
72+ /// Analyze a file using OpenAI API
73+ pub async fn analyze_file_via_api (
74+ client : & Client < OpenAIConfig > ,
75+ model : & str ,
76+ file : & crate :: multi_step_integration:: ParsedFile ,
77+ ) -> Result < Value > {
78+ // Delegate to the existing function for now
79+ crate :: multi_step_integration:: call_analyze_function ( client, model, file) . await
80+ }
81+
82+ /// Helper: Categorize file by path
83+ pub fn categorize_file ( path : & str ) -> FileCategory {
84+ // Implement locally for now to avoid private function call
85+ let path_lower = path. to_lowercase ( ) ;
86+
87+ if path_lower. ends_with ( "test.rs" )
88+ || path_lower. ends_with ( "_test.rs" )
89+ || path_lower. contains ( "tests/" )
90+ || path_lower. ends_with ( ".test.js" )
91+ || path_lower. ends_with ( ".spec.js" )
92+ {
93+ FileCategory :: Test
94+ } else if path_lower. ends_with ( ".md" ) || path_lower. ends_with ( ".rst" ) || path_lower. ends_with ( ".txt" ) {
95+ FileCategory :: Docs
96+ } else if path_lower. ends_with ( "Cargo.toml" )
97+ || path_lower. ends_with ( "package.json" )
98+ || path_lower. ends_with ( "Makefile" )
99+ || path_lower. ends_with ( "build.gradle" )
100+ || path_lower. contains ( "cmake" )
101+ {
102+ FileCategory :: Build
103+ } else if path_lower. ends_with ( ".yml" )
104+ || path_lower. ends_with ( ".yaml" )
105+ || path_lower. ends_with ( ".json" )
106+ || path_lower. ends_with ( ".toml" )
107+ || path_lower. ends_with ( ".ini" )
108+ || path_lower. ends_with ( ".cfg" )
109+ || path_lower. ends_with ( ".conf" )
110+ || path_lower. contains ( "config" )
111+ || path_lower. contains ( ".github/" )
112+ {
113+ FileCategory :: Config
114+ } else if path_lower. ends_with ( ".png" )
115+ || path_lower. ends_with ( ".jpg" )
116+ || path_lower. ends_with ( ".gif" )
117+ || path_lower. ends_with ( ".ico" )
118+ || path_lower. ends_with ( ".pdf" )
119+ || path_lower. ends_with ( ".zip" )
120+ {
121+ FileCategory :: Binary
122+ } else {
123+ FileCategory :: Source
124+ }
125+ }
126+
127+ // Conversion from old FileAnalysisResult to new FileAnalysis
128+ impl From < crate :: multi_step_analysis:: FileAnalysisResult > for FileAnalysis {
129+ fn from ( result : crate :: multi_step_analysis:: FileAnalysisResult ) -> Self {
130+ FileAnalysis {
131+ lines_added : result. lines_added ,
132+ lines_removed : result. lines_removed ,
133+ category : FileCategory :: from ( result. file_category . as_str ( ) ) ,
134+ summary : result. summary ,
135+ }
136+ }
137+ }
138+
139+ #[ cfg( test) ]
140+ mod tests {
141+ use super :: * ;
142+
143+ #[ test]
144+ fn test_file_categorization ( ) {
145+ assert_eq ! ( categorize_file( "src/main.rs" ) , FileCategory :: Source ) ;
146+ assert_eq ! ( categorize_file( "tests/integration_test.rs" ) , FileCategory :: Test ) ;
147+ assert_eq ! ( categorize_file( "package.json" ) , FileCategory :: Build ) ;
148+ assert_eq ! ( categorize_file( ".github/workflows/ci.yml" ) , FileCategory :: Config ) ;
149+ assert_eq ! ( categorize_file( "README.md" ) , FileCategory :: Docs ) ;
150+ assert_eq ! ( categorize_file( "logo.png" ) , FileCategory :: Binary ) ;
151+ }
152+ }
0 commit comments