Skip to content

Commit 547df06

Browse files
committed
update parser for with latest grammar
1 parent ed711a6 commit 547df06

2 files changed

Lines changed: 70 additions & 81 deletions

File tree

‎resources/sshdconfig/locales/en-us.toml‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ set = "Set command: '%{input}'"
3333
[parser]
3434
failedToParse = "failed to parse: '%{input}'"
3535
failedToParseAsArray = "value is not an array"
36-
failedToParseChildNode = "failed to parse child node: '%{input}'"
3736
failedToParseNode = "failed to parse '%{input}'"
3837
failedToParseRoot = "failed to parse root: '%{input}'"
3938
invalidConfig = "invalid config: '%{input}'"

‎resources/sshdconfig/src/parser.rs‎

Lines changed: 70 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@ pub struct SshdConfigParser {
3434
pub 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

Comments
 (0)