(arg_node: tree_sitter::Node, input: &str, input_bytes: &[u8], keyword_info: &KeywordInfo)
| 262 | } |
| 263 | |
| 264 | fn parse_arguments_node(arg_node: tree_sitter::Node, input: &str, input_bytes: &[u8], keyword_info: &KeywordInfo) -> Result<Value, SshdConfigError> { |
| 265 | let mut cursor = arg_node.walk(); |
| 266 | let mut vec: Vec<Value> = Vec::new(); |
| 267 | let is_vec = keyword_info.is_multi_arg(); |
| 268 | |
| 269 | // if there is more than one argument, but a vector is not expected for the keyword, throw an error |
| 270 | let children: Vec<_> = arg_node.named_children(&mut cursor).collect(); |
| 271 | if children.len() > 1 && !is_vec { |
| 272 | return Err(SshdConfigError::ParserError(t!("parser.invalidMultiArgNode", input = input).to_string())); |
| 273 | } |
| 274 | |
| 275 | for node in &children { |
| 276 | if node.is_error() { |
| 277 | return Err(SshdConfigError::ParserError(t!("parser.failedToParseNode", input = input).to_string())); |
| 278 | } |
| 279 | let arg = node.utf8_text(input_bytes)?; |
| 280 | match node.kind() { |
| 281 | "boolean" => { |
| 282 | let arg_str = arg.trim(); |
| 283 | vec.push(Value::Bool(arg_str.eq_ignore_ascii_case("yes"))); |
| 284 | } |
| 285 | "string" => { |
| 286 | let arg_str = arg.trim(); |
| 287 | // Unescape backslashes (sshd -T escapes them on Windows) |
| 288 | let unescaped = unescape_backslashes(arg_str); |
| 289 | vec.push(Value::String(unescaped)); |
| 290 | }, |
| 291 | "number" => { |
| 292 | vec.push(Value::Number(arg.parse::<u64>()?.into())); |
| 293 | }, |
| 294 | _ => return Err(SshdConfigError::ParserError(t!("parser.unknownNode", kind = node.kind()).to_string())) |
| 295 | } |
| 296 | } |
| 297 | |
| 298 | // Handle structured keywords (e.g., subsystem) |
| 299 | if keyword_info.requires_structured_format() { |
| 300 | if vec.len() < 2 { |
| 301 | return Err(SshdConfigError::ParserError( |
| 302 | t!("parser.structuredKeywordRequiresMinArgs", keyword = &keyword_info.name).to_string() |
| 303 | )); |
| 304 | } |
| 305 | |
| 306 | // Extract name (first element) and value (remaining elements joined with space) |
| 307 | let name = vec[0].clone(); |
| 308 | let value_parts: Vec<String> = vec[1..].iter() |
| 309 | .filter_map(|v| { |
| 310 | match v { |
| 311 | Value::String(s) => Some(s.clone()), |
| 312 | Value::Number(n) => Some(n.to_string()), |
| 313 | Value::Bool(b) => Some(if *b { "yes".to_string() } else { "no".to_string() }), |
| 314 | _ => None |
| 315 | } |
| 316 | }) |
| 317 | .collect(); |
| 318 | let value_str = value_parts.join(" "); |
| 319 | |
| 320 | let mut structured = Map::new(); |
| 321 | structured.insert("name".to_string(), name); |
no test coverage detected