@@ -50,7 +50,7 @@ impl<'a> RuleSynthesizer<'a> {
5050
5151 // Parse YAML rules from response
5252 let yaml_content = response. content ;
53- let rules = self . parse_yaml_rules ( & yaml_content, language) ?;
53+ let rules = parse_yaml_rules ( & yaml_content, language) ?;
5454
5555 // Validate each rule and collect valid ones
5656 let mut valid_rules = Vec :: new ( ) ;
@@ -75,82 +75,83 @@ impl<'a> RuleSynthesizer<'a> {
7575
7676 // Persist valid rules
7777 if !valid_rules. is_empty ( ) {
78- self . persist_rules ( & valid_rules, cwe, language) ?;
78+ persist_rules (
79+ & valid_rules,
80+ cwe,
81+ language,
82+ self . config . output_dir . to_string_lossy ( ) . as_ref ( ) ,
83+ ) ?;
7984 }
8085
8186 Ok ( valid_rules)
8287 }
88+ }
8389
84- /// Parse YAML rules from LLM response
85- fn parse_yaml_rules (
86- & self ,
87- yaml_content : & str ,
88- _language : & str ,
89- ) -> Result < Vec < String > , RuleError > {
90- let mut rules = Vec :: new ( ) ;
91- let mut current_rule = String :: new ( ) ;
92- let mut in_rule = false ;
93-
94- for line in yaml_content. lines ( ) {
95- if line. trim ( ) == "---" {
96- if !current_rule. is_empty ( ) {
97- rules. push ( current_rule. trim ( ) . to_string ( ) ) ;
98- current_rule = String :: new ( ) ;
99- }
100- in_rule = true ;
101- continue ;
90+ /// Parse YAML rules from LLM response
91+ pub fn parse_yaml_rules ( yaml_content : & str , _language : & str ) -> Result < Vec < String > , RuleError > {
92+ let mut rules = Vec :: new ( ) ;
93+ let mut current_rule = String :: new ( ) ;
94+ let mut in_rule = false ;
95+
96+ for line in yaml_content. lines ( ) {
97+ if line. trim ( ) == "---" {
98+ if !current_rule. is_empty ( ) {
99+ rules. push ( current_rule. trim ( ) . to_string ( ) ) ;
100+ current_rule = String :: new ( ) ;
102101 }
102+ in_rule = true ;
103+ continue ;
104+ }
103105
104- if in_rule || line. trim ( ) . starts_with ( "rules:" ) {
105- let should_add = !current_rule. is_empty ( ) || !line. trim ( ) . is_empty ( ) ;
106- if should_add {
107- current_rule. push_str ( line) ;
108- current_rule. push ( '\n' ) ;
109- }
106+ if in_rule || line. trim ( ) . starts_with ( "rules:" ) {
107+ let should_add = !current_rule. is_empty ( ) || !line. trim ( ) . is_empty ( ) ;
108+ if should_add {
109+ current_rule. push_str ( line) ;
110+ current_rule. push ( '\n' ) ;
110111 }
111112 }
113+ }
112114
113- // Push last rule
114- if !current_rule. is_empty ( ) {
115- rules. push ( current_rule. trim ( ) . to_string ( ) ) ;
116- }
115+ // Push last rule
116+ if !current_rule. is_empty ( ) {
117+ rules. push ( current_rule. trim ( ) . to_string ( ) ) ;
118+ }
117119
118- // If no rules found, try to parse as single rule
119- if rules. is_empty ( ) && !yaml_content. trim ( ) . is_empty ( ) {
120- rules. push ( yaml_content. trim ( ) . to_string ( ) ) ;
121- }
120+ // If no rules found, try to parse as single rule
121+ if rules. is_empty ( ) && !yaml_content. trim ( ) . is_empty ( ) {
122+ rules. push ( yaml_content. trim ( ) . to_string ( ) ) ;
123+ }
122124
123- Ok ( rules)
124- }
125-
126- /// Persist valid rules to output directory
127- fn persist_rules (
128- & self ,
129- rules : & [ SemgrepRule ] ,
130- cwe : & str ,
131- language : & str ,
132- ) -> Result < ( ) , RuleError > {
133- let output_dir = PathBuf :: from ( & self . config . output_dir ) ;
134- std:: fs:: create_dir_all ( & output_dir)
135- . map_err ( |e| RuleError :: IoError ( format ! ( "Failed to create output directory: {}" , e) ) ) ?;
136-
137- for ( i, rule) in rules. iter ( ) . enumerate ( ) {
138- let filename = format ! ( "{}_{}_{}.yml" , cwe, language, i) ;
139- let filepath = output_dir. join ( & filename) ;
140-
141- std:: fs:: write ( & filepath, & rule. yaml ) . map_err ( |e| {
142- RuleError :: IoError ( format ! (
143- "Failed to write rule to {}: {}" ,
144- filepath. display( ) ,
145- e
146- ) )
147- } ) ?;
148-
149- tracing:: info!( "Persisted rule: {}" , filepath. display( ) ) ;
150- }
125+ Ok ( rules)
126+ }
151127
152- Ok ( ( ) )
153- }
128+ /// Persist valid rules to output directory
129+ pub fn persist_rules (
130+ rules : & [ SemgrepRule ] ,
131+ cwe : & str ,
132+ language : & str ,
133+ output_dir : & str ,
134+ ) -> Result < ( ) , RuleError > {
135+ let output_dir = PathBuf :: from ( output_dir) ;
136+ std:: fs:: create_dir_all ( & output_dir)
137+ . map_err ( |e| RuleError :: IoError ( format ! ( "Failed to create output directory: {}" , e) ) ) ?;
138+
139+ for ( i, rule) in rules. iter ( ) . enumerate ( ) {
140+ let filename = format ! ( "{}_{}_{}.yml" , cwe, language, i) ;
141+ let filepath = output_dir. join ( & filename) ;
142+
143+ std:: fs:: write ( & filepath, & rule. yaml ) . map_err ( |e| {
144+ RuleError :: IoError ( format ! (
145+ "Failed to write rule to {}: {}" ,
146+ filepath. display( ) ,
147+ e
148+ ) )
149+ } ) ?;
150+
151+ tracing:: info!( "Persisted rule: {}" , filepath. display( ) ) ;
152+ }
153+
154+ Ok ( ( ) )
154155}
155156
156157/// Extract rule ID from YAML content
0 commit comments