@@ -34,9 +34,7 @@ pub struct SshdConfigParser {
3434pub fn parse_text_to_map ( input : & str ) -> Result < Map < String , Value > , SshdConfigError > {
3535 let mut parser = SshdConfigParser :: new ( ) ;
3636 parser. parse_text ( input) ?;
37- let lowercased_map = parser. map . into_iter ( )
38- . map ( |( k, v) | ( k. to_lowercase ( ) , v) )
39- . collect ( ) ;
37+ let lowercased_map = parser. map . into_iter ( ) . map ( |( k, v) | ( k. to_lowercase ( ) , v) ) . collect ( ) ;
4038 Ok ( lowercased_map)
4139}
4240
@@ -88,94 +86,101 @@ impl SshdConfigParser {
8886 Self :: parse_and_insert_keyword ( node, input, input_bytes, Some ( & mut self . map ) ) ?;
8987 Ok ( ( ) )
9088 } ,
91- "comment" | "empty_line" => Ok ( ( ) ) ,
89+ "comment" => Ok ( ( ) ) ,
9290 "match" => self . parse_match_node ( node, input, input_bytes) ,
9391 _ => Err ( SshdConfigError :: ParserError ( t ! ( "parser.unknownNodeType" , node = node. kind( ) ) . to_string ( ) ) ) ,
9492 }
9593 }
9694
9795 fn parse_match_node ( & mut self , match_node : tree_sitter:: Node , input : & str , input_bytes : & [ u8 ] ) -> Result < ( ) , SshdConfigError > {
98- let Some ( criteria_node) = match_node. child_by_field_name ( "criteria" ) else {
99- return Err ( SshdConfigError :: ParserError (
100- t ! ( "parser.missingCriteriaInMatch" , input = input) . to_string ( )
101- ) ) ;
102- } ;
103-
104- // Parse criteria by extracting the entire line and parsing key-value pairs
105- let criteria_map = Self :: parse_match_criteria ( criteria_node, input, input_bytes) ?;
106-
96+ let mut criteria_map = Map :: new ( ) ;
97+ let mut cursor = match_node. walk ( ) ;
10798 let mut match_object = Map :: new ( ) ;
108- match_object. insert ( "criteria" . to_string ( ) , Value :: Object ( criteria_map) ) ;
10999
110- // Collect keywords - parse and insert directly into match_object
111- let mut cursor = match_node. walk ( ) ;
112100 for child_node in match_node. named_children ( & mut cursor) {
113101 if child_node. is_error ( ) {
114- return Err ( SshdConfigError :: ParserError (
115- t ! ( "parser.failedToParseChildNode" , input = input) . to_string ( )
116- ) ) ;
102+ return Err ( SshdConfigError :: ParserError ( t ! ( "parser.failedToParseNode" , input = input) . to_string ( ) ) ) ;
117103 }
118104
119105 match child_node. kind ( ) {
106+ "comment" => { }
107+ "criteria" => {
108+ Self :: parse_match_criteria ( child_node, input, input_bytes, & mut criteria_map) ?;
109+ }
120110 "keyword" => {
121- // Skip the criteria node (already processed)
122- if child_node. id ( ) == criteria_node. id ( ) {
123- continue ;
124- }
125111 Self :: parse_and_insert_keyword ( child_node, input, input_bytes, Some ( & mut match_object) ) ?;
126112 }
127- "comment" => { }
128113 _ => {
129114 return Err ( SshdConfigError :: ParserError ( t ! ( "parser.unknownNodeType" , node = child_node. kind( ) ) . to_string ( ) ) ) ;
130115 }
131116 }
132117 }
133118
134119 // Add the match object to the main map
120+ if criteria_map. is_empty ( ) {
121+ return Err ( SshdConfigError :: ParserError ( t ! ( "parser.missingCriteriaInMatch" , input = input) . to_string ( ) ) ) ;
122+ }
123+ match_object. insert ( "criteria" . to_string ( ) , Value :: Object ( criteria_map) ) ;
135124 Self :: insert_into_map ( & mut self . map , "match" , Value :: Object ( match_object) , true ) ?;
136125 Ok ( ( ) )
137126 }
138127
139- /// Parse match criteria which can contain multiple key-value pairs on a single line.
140- /// Example: "user alice,bob address *.*.0.1 localport 22"
141- /// Returns a Map with each criterion as a key with an array value.
142- fn parse_match_criteria ( criteria_node : tree_sitter:: Node , input : & str , input_bytes : & [ u8 ] ) -> Result < Map < String , Value > , SshdConfigError > {
143- let Ok ( criteria_text) = criteria_node. utf8_text ( input_bytes) else {
144- return Err ( SshdConfigError :: ParserError ( t ! ( "parser.failedToParseChildNode" , input = input) . to_string ( ) ) ) ;
145- } ;
128+ /// Parse a single match criteria node and insert into the provided criteria map.
129+ /// Example criteria node: "user alice,bob" or "address *.*.0.1"
130+ /// Inserts the criterion as a key with an array value into the `criteria_map`.
131+ fn parse_match_criteria ( criteria_node : tree_sitter:: Node , input : & str , input_bytes : & [ u8 ] , criteria_map : & mut Map < String , Value > ) -> Result < ( ) , SshdConfigError > {
132+ let mut cursor = criteria_node. walk ( ) ;
133+ let mut key: Option < String > = None ;
134+ let mut values: Vec < Value > = Vec :: new ( ) ;
135+
136+ // Iterate through named children of the criteria node
137+ for child in criteria_node. named_children ( & mut cursor) {
138+ if child. is_error ( ) {
139+ return Err ( SshdConfigError :: ParserError ( t ! ( "parser.failedToParseNode" , input = input) . to_string ( ) ) ) ;
140+ }
146141
147- let criteria_text = criteria_text. trim_end ( ) ;
148- let tokens: Vec < & str > = criteria_text. split_whitespace ( ) . collect ( ) ;
149- let mut criteria_map = Map :: new ( ) ;
150- let mut i = 0 ;
151-
152- while i < tokens. len ( ) {
153- let key = tokens[ i] . to_lowercase ( ) ;
154- i += 1 ;
155- if i >= tokens. len ( ) {
156- return Err ( SshdConfigError :: ParserError (
157- t ! ( "parser.missingValueInChildNode" , input = input) . to_string ( )
158- ) ) ;
142+ match child. kind ( ) {
143+ "alpha" => {
144+ let Ok ( text) = child. utf8_text ( input_bytes) else {
145+ return Err ( SshdConfigError :: ParserError ( t ! ( "parser.failedToParseNode" , input = input) . to_string ( ) ) ) ;
146+ } ;
147+ key = Some ( text. to_string ( ) ) ;
148+ }
149+ "boolean" | "string" => {
150+ let Ok ( arg) = child. utf8_text ( input_bytes) else {
151+ return Err ( SshdConfigError :: ParserError ( t ! ( "parser.failedToParseNode" , input = input) . to_string ( ) ) ) ;
152+ } ;
153+ let arg_str = arg. trim ( ) ;
154+ values. push ( Value :: String ( arg_str. to_string ( ) ) ) ;
155+ }
156+ "number" => {
157+ let Ok ( arg) = child. utf8_text ( input_bytes) else {
158+ return Err ( SshdConfigError :: ParserError ( t ! ( "parser.failedToParseNode" , input = input) . to_string ( ) ) ) ;
159+ } ;
160+ values. push ( Value :: Number ( arg. parse :: < u64 > ( ) ?. into ( ) ) ) ;
161+ }
162+ _ => {
163+ return Err ( SshdConfigError :: ParserError ( t ! ( "parser.failedToParseNode" , input = input) . to_string ( ) ) ) ;
164+ }
159165 }
166+ }
160167
161- let value_str = tokens[ i] ;
162- let values: Vec < Value > = value_str. split ( ',' ) . map ( |s| Value :: String ( s. to_string ( ) ) ) . collect ( ) ;
168+ let Some ( criteria_key) = key else {
169+ return Err ( SshdConfigError :: ParserError ( t ! ( "parser.failedToParseNode" , input = input) . to_string ( ) ) ) ;
170+ } ;
163171
164- criteria_map . insert ( key , Value :: Array ( values ) ) ;
165- i += 1 ;
172+ if values . is_empty ( ) {
173+ return Err ( SshdConfigError :: ParserError ( t ! ( "parser.noArgumentsFound" , input = input ) . to_string ( ) ) ) ;
166174 }
167- Ok ( criteria_map)
175+
176+ criteria_map. insert ( criteria_key. to_lowercase ( ) , Value :: Array ( values) ) ;
177+ Ok ( ( ) )
168178 }
169179
170180 /// Parse a keyword node and optionally insert it into a map.
171181 /// If `target_map` is provided, the keyword will be inserted into that map with repeatability handling.
172182 /// If `target_map` is None, returns the key-value pair without inserting.
173- fn parse_and_insert_keyword (
174- keyword_node : tree_sitter:: Node ,
175- input : & str ,
176- input_bytes : & [ u8 ] ,
177- target_map : Option < & mut Map < String , Value > >
178- ) -> Result < ( String , Value ) , SshdConfigError > {
183+ fn parse_and_insert_keyword ( keyword_node : tree_sitter:: Node , input : & str , input_bytes : & [ u8 ] , target_map : Option < & mut Map < String , Value > > ) -> Result < ( String , Value ) , SshdConfigError > {
179184 let mut cursor = keyword_node. walk ( ) ;
180185 let mut key = None ;
181186 let mut value = Value :: Null ;
@@ -186,9 +191,7 @@ impl SshdConfigParser {
186191
187192 if let Some ( keyword) = keyword_node. child_by_field_name ( "keyword" ) {
188193 let Ok ( text) = keyword. utf8_text ( input_bytes) else {
189- return Err ( SshdConfigError :: ParserError (
190- t ! ( "parser.failedToParseChildNode" , input = input) . to_string ( )
191- ) ) ;
194+ return Err ( SshdConfigError :: ParserError ( t ! ( "parser.failedToParseNode" , input = input) . to_string ( ) ) ) ;
192195 } ;
193196
194197 if target_map. is_some ( ) {
@@ -208,16 +211,14 @@ impl SshdConfigParser {
208211 // Check for operator field
209212 if let Some ( operator_node) = keyword_node. child_by_field_name ( "operator" ) {
210213 let Ok ( op_text) = operator_node. utf8_text ( input_bytes) else {
211- return Err ( SshdConfigError :: ParserError (
212- t ! ( "parser.failedToParseChildNode" , input = input) . to_string ( )
213- ) ) ;
214+ return Err ( SshdConfigError :: ParserError ( t ! ( "parser.failedToParseNode" , input = input) . to_string ( ) ) ) ;
214215 } ;
215216 operator = Some ( op_text. to_string ( ) ) ;
216217 }
217218
218219 for node in keyword_node. named_children ( & mut cursor) {
219220 if node. is_error ( ) {
220- return Err ( SshdConfigError :: ParserError ( t ! ( "parser.failedToParseChildNode " , input = input) . to_string ( ) ) ) ;
221+ return Err ( SshdConfigError :: ParserError ( t ! ( "parser.failedToParseNode " , input = input) . to_string ( ) ) ) ;
221222 }
222223 if node. kind ( ) == "arguments" {
223224 value = parse_arguments_node ( node, input, input_bytes, is_vec, keyword_type) ?;
@@ -289,37 +290,25 @@ impl SshdConfigParser {
289290 }
290291}
291292
292- fn parse_arguments_node ( arg_node : tree_sitter:: Node , input : & str , input_bytes : & [ u8 ] , is_vec : bool , keyword_type : KeywordType ) -> Result < Value , SshdConfigError > {
293+ fn parse_arguments_node ( arg_node : tree_sitter:: Node , input : & str , input_bytes : & [ u8 ] , is_vec : bool , _keyword_type : KeywordType ) -> Result < Value , SshdConfigError > {
293294 let mut cursor = arg_node. walk ( ) ;
294295 let mut vec: Vec < Value > = Vec :: new ( ) ;
295-
296296 // if there is more than one argument, but a vector is not expected for the keyword, throw an error
297297 let children: Vec < _ > = arg_node. named_children ( & mut cursor) . collect ( ) ;
298298 if children. len ( ) > 1 && !is_vec {
299299 return Err ( SshdConfigError :: ParserError ( t ! ( "parser.invalidMultiArgNode" , input = input) . to_string ( ) ) ) ;
300300 }
301-
302301 for node in & children {
303302 if node. is_error ( ) {
304- return Err ( SshdConfigError :: ParserError ( t ! ( "parser.failedToParseChildNode " , input = input) . to_string ( ) ) ) ;
303+ return Err ( SshdConfigError :: ParserError ( t ! ( "parser.failedToParseNode " , input = input) . to_string ( ) ) ) ;
305304 }
306305 match node. kind ( ) {
307- "boolean" | "string" | "quotedString" => {
306+ "boolean" | "string" => {
308307 let Ok ( arg) = node. utf8_text ( input_bytes) else {
309- return Err ( SshdConfigError :: ParserError (
310- t ! ( "parser.failedToParseNode" , input = input) . to_string ( )
311- ) ) ;
308+ return Err ( SshdConfigError :: ParserError ( t ! ( "parser.failedToParseNode" , input = input) . to_string ( ) ) ) ;
312309 } ;
313310 let arg_str = arg. trim ( ) ;
314-
315- // For space-separated keywords, split unquoted strings on whitespace
316- if node. kind ( ) == "string" && keyword_type == KeywordType :: SpaceSeparated && is_vec {
317- for token in arg_str. split_whitespace ( ) {
318- vec. push ( Value :: String ( token. to_string ( ) ) ) ;
319- }
320- } else {
321- vec. push ( Value :: String ( arg_str. to_string ( ) ) ) ;
322- }
311+ vec. push ( Value :: String ( arg_str. to_string ( ) ) ) ;
323312 } ,
324313 "number" => {
325314 let Ok ( arg) = node. utf8_text ( input_bytes) else {
@@ -334,7 +323,6 @@ fn parse_arguments_node(arg_node: tree_sitter::Node, input: &str, input_bytes: &
334323 _ => return Err ( SshdConfigError :: ParserError ( t ! ( "parser.unknownNode" , kind = node. kind( ) ) . to_string ( ) ) )
335324 }
336325 }
337-
338326 // Always return array if is_vec is true (for MULTI_ARG_KEYWORDS_COMMA_SEP, MULTI_ARG_KEYWORDS_SPACE_SEP, and REPEATABLE_KEYWORDS)
339327 if is_vec {
340328 Ok ( Value :: Array ( vec) )
@@ -377,7 +365,7 @@ mod tests {
377365 let result: Map < String , Value > = parse_text_to_map ( input) . unwrap ( ) ;
378366 let expected = vec ! [
379367 Value :: String ( "administrators" . to_string( ) ) ,
380- Value :: String ( "\" openssh users\" " . to_string( ) ) ,
368+ Value :: String ( "openssh users" . to_string( ) ) ,
381369 ] ;
382370 assert_eq ! ( result. get( "allowgroups" ) . unwrap( ) , & Value :: Array ( expected) ) ;
383371 }
@@ -459,6 +447,7 @@ match user bob
459447 allowtcpforwarding yes
460448"# ;
461449 let result: Map < String , Value > = parse_text_to_map ( input) . unwrap ( ) ;
450+ println ! ( "{:#?}" , result) ;
462451 let match_array = result. get ( "match" ) . unwrap ( ) . as_array ( ) . unwrap ( ) ;
463452 assert_eq ! ( match_array. len( ) , 1 ) ;
464453 let match_obj = match_array[ 0 ] . as_object ( ) . unwrap ( ) ;
@@ -503,6 +492,7 @@ match user alice,bob
503492 let match_obj = match_array[ 0 ] . as_object ( ) . unwrap ( ) ;
504493 let criteria = match_obj. get ( "criteria" ) . unwrap ( ) . as_object ( ) . unwrap ( ) ;
505494 let user_array = criteria. get ( "user" ) . unwrap ( ) . as_array ( ) . unwrap ( ) ;
495+ println ! ( "{:#?}" , user_array) ;
506496 assert_eq ! ( user_array. len( ) , 2 ) ;
507497 assert_eq ! ( user_array[ 0 ] , Value :: String ( "alice" . to_string( ) ) ) ;
508498 assert_eq ! ( user_array[ 1 ] , Value :: String ( "bob" . to_string( ) ) ) ;
0 commit comments