Skip to content

Commit 378acaf

Browse files
committed
address Steve's feedback
1 parent c945d95 commit 378acaf

3 files changed

Lines changed: 38 additions & 64 deletions

File tree

resources/sshdconfig/locales/en-us.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ parser = "Parser"
1717
parseInt = "Parse Integer"
1818
persist = "Persist"
1919
registry = "Registry"
20+
stringUtf8 = "String UTF-8"
2021

2122
[get]
2223
debugSetting = "Get setting:"
@@ -42,8 +43,10 @@ invalidMultiArgNode = "multi-arg node '%{input}' is not valid"
4243
keyNotFound = "key '%{key}' not found"
4344
keyNotRepeatable = "key '%{key}' is not repeatable"
4445
missingCriteriaInMatch = "missing criteria field in match block: '%{input}'"
45-
missingValueInChildNode = "missing value in child node: '%{input}'"
4646
missingKeyInChildNode = "missing key in child node: '%{input}'"
47+
missingKeyInCriteria = "missing key in criteria node: '%{input}'"
48+
missingValueInCriteria = "missing value in criteria node: '%{input}'"
49+
missingValueInChildNode = "missing value in child node: '%{input}'"
4750
noArgumentsFound = "no arguments found in node: '%{input}'"
4851
valueDebug = "Parsed argument value:"
4952
unknownNode = "unknown node: '%{kind}'"

resources/sshdconfig/src/error.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ pub enum SshdConfigError {
1111
CommandError(String),
1212
#[error("{t}: {0}", t = t!("error.fmt"))]
1313
FmtError(#[from] std::fmt::Error),
14+
#[error("{t}: {0}", t = t!("error.envVar"))]
15+
EnvVarError(#[from] std::env::VarError),
1416
#[error("{t}: {0}", t = t!("error.invalidInput"))]
1517
InvalidInput(String),
1618
#[error("{t}: {0}", t = t!("error.io"))]
@@ -28,6 +30,6 @@ pub enum SshdConfigError {
2830
#[cfg(windows)]
2931
#[error("{t}: {0}", t = t!("error.registry"))]
3032
RegistryError(#[from] dsc_lib_registry::error::RegistryError),
31-
#[error("{t}: {0}", t = t!("error.envVar"))]
32-
EnvVarError(#[from] std::env::VarError),
33+
#[error("{t}: {0}", t = t!("error.stringUtf8"))]
34+
StringUtf8Error(#[from] std::str::Utf8Error),
3335
}

resources/sshdconfig/src/parser.rs

Lines changed: 30 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -106,52 +106,25 @@ impl SshdConfigParser {
106106
/// Example criteria node: "user alice,bob" or "address *.*.0.1"
107107
/// Inserts the criterion as a key with an array value into the `criteria_map`.
108108
fn parse_match_criteria(criteria_node: tree_sitter::Node, input: &str, input_bytes: &[u8], criteria_map: &mut Map<String, Value>) -> Result<(), SshdConfigError> {
109-
let mut cursor = criteria_node.walk();
110-
let mut key: Option<String> = None;
111-
let mut values: Vec<Value> = Vec::new();
112-
113-
// Iterate through named children of the criteria node
114-
for child in criteria_node.named_children(&mut cursor) {
115-
if child.is_error() {
109+
if let Some(key_node) = criteria_node.child_by_field_name("keyword") {
110+
let Ok(key_text) = key_node.utf8_text(input_bytes) else {
116111
return Err(SshdConfigError::ParserError(t!("parser.failedToParseNode", input = input).to_string()));
117-
}
112+
};
113+
let key = key_text.to_string();
118114

119-
match child.kind() {
120-
"alpha" => {
121-
let Ok(text) = child.utf8_text(input_bytes) else {
122-
return Err(SshdConfigError::ParserError(t!("parser.failedToParseNode", input = input).to_string()));
123-
};
124-
key = Some(text.to_string());
125-
}
126-
"boolean" | "string" => {
127-
let Ok(arg) = child.utf8_text(input_bytes) else {
128-
return Err(SshdConfigError::ParserError(t!("parser.failedToParseNode", input = input).to_string()));
129-
};
130-
let arg_str = arg.trim();
131-
values.push(Value::String(arg_str.to_string()));
132-
}
133-
"number" => {
134-
let Ok(arg) = child.utf8_text(input_bytes) else {
135-
return Err(SshdConfigError::ParserError(t!("parser.failedToParseNode", input = input).to_string()));
136-
};
137-
values.push(Value::Number(arg.parse::<u64>()?.into()));
138-
}
139-
_ => {
140-
return Err(SshdConfigError::ParserError(t!("parser.failedToParseNode", input = input).to_string()));
141-
}
115+
let values: Value;
116+
if let Some(value_node) = criteria_node.child_by_field_name("argument") {
117+
values = parse_arguments_node(value_node, input, input_bytes, true)?;
118+
}
119+
else {
120+
return Err(SshdConfigError::ParserError(t!("parser.missingValueInCriteria", input = input).to_string()));
142121
}
143-
}
144-
145-
let Some(criteria_key) = key else {
146-
return Err(SshdConfigError::ParserError(t!("parser.failedToParseNode", input = input).to_string()));
147-
};
148122

149-
if values.is_empty() {
150-
return Err(SshdConfigError::ParserError(t!("parser.noArgumentsFound", input = input).to_string()));
123+
criteria_map.insert(key.to_lowercase(), values);
124+
Ok(())
125+
} else {
126+
Err(SshdConfigError::ParserError(t!("parser.missingKeyInCriteria", input = input).to_string()))
151127
}
152-
153-
criteria_map.insert(criteria_key.to_lowercase(), Value::Array(values));
154-
Ok(())
155128
}
156129

157130
/// Parse a keyword node and optionally insert it into a map.
@@ -252,7 +225,7 @@ impl SshdConfigParser {
252225
}
253226
} else if is_repeatable {
254227
// Initialize repeatable keywords as arrays
255-
if let Value::Array(_) = value {
228+
if value.is_array() {
256229
map.insert(key.to_string(), value);
257230
} else {
258231
map.insert(key.to_string(), Value::Array(vec![value]));
@@ -276,22 +249,17 @@ fn parse_arguments_node(arg_node: tree_sitter::Node, input: &str, input_bytes: &
276249
if node.is_error() {
277250
return Err(SshdConfigError::ParserError(t!("parser.failedToParseNode", input = input).to_string()));
278251
}
252+
let arg = node.utf8_text(input_bytes)?;
279253
match node.kind() {
280-
"boolean" | "string" => {
281-
let Ok(arg) = node.utf8_text(input_bytes) else {
282-
return Err(SshdConfigError::ParserError(
283-
t!("parser.failedToParseNode", input = input).to_string()
284-
));
285-
};
254+
"boolean" => {
255+
let arg_str = arg.trim();
256+
vec.push(Value::Bool(arg_str.eq_ignore_ascii_case("yes")));
257+
}
258+
"string" => {
286259
let arg_str = arg.trim();
287260
vec.push(Value::String(arg_str.to_string()));
288261
},
289262
"number" => {
290-
let Ok(arg) = node.utf8_text(input_bytes) else {
291-
return Err(SshdConfigError::ParserError(
292-
t!("parser.failedToParseNode", input = input).to_string()
293-
));
294-
};
295263
vec.push(Value::Number(arg.parse::<u64>()?.into()));
296264
},
297265
_ => return Err(SshdConfigError::ParserError(t!("parser.unknownNode", kind = node.kind()).to_string()))
@@ -366,7 +334,7 @@ mod tests {
366334
fn bool_keyword() {
367335
let input = "printmotd yes\r\n";
368336
let result: Map<String, Value> = parse_text_to_map(input).unwrap();
369-
assert_eq!(result.get("printmotd").unwrap(), &Value::String("yes".to_string()));
337+
assert_eq!(result.get("printmotd").unwrap(), &Value::Bool(true));
370338
}
371339

372340
#[test]
@@ -442,11 +410,12 @@ match user bob
442410
let match_array = result.get("match").unwrap().as_array().unwrap();
443411
assert_eq!(match_array.len(), 1);
444412
let match_obj = match_array[0].as_object().unwrap();
413+
println!("match_obj: {:?}", match_obj);
445414
let criteria = match_obj.get("criteria").unwrap().as_object().unwrap();
446415
let user_array = criteria.get("user").unwrap().as_array().unwrap();
447416
assert_eq!(user_array[0], Value::String("bob".to_string()));
448-
assert_eq!(match_obj.get("gssapiauthentication").unwrap(), &Value::String("yes".to_string()));
449-
assert_eq!(match_obj.get("allowtcpforwarding").unwrap(), &Value::String("yes".to_string()));
417+
assert_eq!(match_obj.get("gssapiauthentication").unwrap(), &Value::Bool(true));
418+
assert_eq!(match_obj.get("allowtcpforwarding").unwrap(), &Value::Bool(true));
450419
}
451420

452421
#[test]
@@ -464,12 +433,12 @@ match group administrators
464433
let criteria1 = match_obj1.get("criteria").unwrap().as_object().unwrap();
465434
let user_array1 = criteria1.get("user").unwrap().as_array().unwrap();
466435
assert_eq!(user_array1[0], Value::String("alice".to_string()));
467-
assert_eq!(match_obj1.get("passwordauthentication").unwrap(), &Value::String("yes".to_string()));
436+
assert_eq!(match_obj1.get("passwordauthentication").unwrap(), &Value::Bool(true));
468437
let match_obj2 = match_array[1].as_object().unwrap();
469438
let criteria2 = match_obj2.get("criteria").unwrap().as_object().unwrap();
470439
let group_array2 = criteria2.get("group").unwrap().as_array().unwrap();
471440
assert_eq!(group_array2[0], Value::String("administrators".to_string()));
472-
assert_eq!(match_obj2.get("permitrootlogin").unwrap(), &Value::String("yes".to_string()));
441+
assert_eq!(match_obj2.get("permitrootlogin").unwrap(), &Value::Bool(true));
473442
}
474443

475444
#[test]
@@ -561,7 +530,7 @@ match user developer
561530
let result: Map<String, Value> = parse_text_to_map(input).unwrap();
562531
let match_array = result.get("match").unwrap().as_array().unwrap();
563532
let match_obj = match_array[0].as_object().unwrap();
564-
assert_eq!(match_obj.get("passwordauthentication").unwrap(), &Value::String("yes".to_string()));
533+
assert_eq!(match_obj.get("passwordauthentication").unwrap(), &Value::Bool(true));
565534
assert_eq!(match_obj.len(), 2);
566535
}
567536

@@ -588,8 +557,8 @@ match user alice,bob address 1.2.3.4/56
588557
assert_eq!(address_array.len(), 1);
589558
assert_eq!(address_array[0], Value::String("1.2.3.4/56".to_string()));
590559

591-
assert_eq!(match_obj.get("passwordauthentication").unwrap(), &Value::String("yes".to_string()));
592-
assert_eq!(match_obj.get("allowtcpforwarding").unwrap(), &Value::String("no".to_string()));
560+
assert_eq!(match_obj.get("passwordauthentication").unwrap(), &Value::Bool(true));
561+
assert_eq!(match_obj.get("allowtcpforwarding").unwrap(), &Value::Bool(false));
593562
}
594563

595564
#[test]

0 commit comments

Comments
 (0)