From 8dffd91678cac80515a1283e00049b4cf2610f94 Mon Sep 17 00:00:00 2001 From: Adam Cimarosti Date: Thu, 11 Apr 2024 15:56:17 +0100 Subject: [PATCH] feat: trailing semicolon is now optional --- questdb-confstr/README.md | 6 +++--- questdb-confstr/src/lib.rs | 7 +++---- questdb-confstr/tests/tests.rs | 20 ++++++++------------ 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/questdb-confstr/README.md b/questdb-confstr/README.md index 03e6262..bb3946e 100644 --- a/questdb-confstr/README.md +++ b/questdb-confstr/README.md @@ -12,7 +12,7 @@ service::key1=value1;key2=value2;key3=value3; ``` A few rules: -* The last semicolon is mandatory. +* The last semicolon is optional. * Service name and keys are case-sensitive. * Keys are ASCII alphanumeric and can contain underscores. * Values are case-sensitive unicode strings which can contain any characters, @@ -21,10 +21,10 @@ A few rules: ## Grammar -```plain +```json conf_str ::= service "::" params | service service ::= identifier -params ::= param (";" param)* ";" +params ::= param (";" param)* ";"? param ::= key "=" value key ::= identifier value ::= { value_char } diff --git a/questdb-confstr/src/lib.rs b/questdb-confstr/src/lib.rs index eedc206..37bf92c 100644 --- a/questdb-confstr/src/lib.rs +++ b/questdb-confstr/src/lib.rs @@ -197,7 +197,6 @@ fn parse_ident( fn parse_value( iter: &mut Peekable2, next_pos: &mut Position, - input_len: usize, ) -> Result { let mut value = String::new(); loop { @@ -220,7 +219,7 @@ fn parse_value( value.push(c); let _ = iter.next(); } - (None, _) => return Err(parse_err(ErrorKind::MissingTrailingSemicolon, input_len)), + (None, _) => break, } } Ok(value) @@ -262,8 +261,8 @@ fn parse_params( Some((p, c)) => return Err(parse_err(ErrorKind::BadSeparator(('=', c)), p)), None => return Err(parse_err(ErrorKind::IncompleteKeyValue, input_len)), } - let value = parse_value(iter, next_pos, input_len)?; - iter.next().unwrap(); // skip ';' + let value = parse_value(iter, next_pos)?; + iter.next(); // skip ';', if present. params.insert(key, value); } Ok(params) diff --git a/questdb-confstr/tests/tests.rs b/questdb-confstr/tests/tests.rs index d65fd56..19eda16 100644 --- a/questdb-confstr/tests/tests.rs +++ b/questdb-confstr/tests/tests.rs @@ -259,23 +259,19 @@ fn test_incomplete_key_no_value() { #[test] fn missing_trailing_semicolon() { let input = "http::host=localhost;port=9000"; - let config = parse_conf_str(input); - assert!(config.is_err()); - let err = config.unwrap_err(); - assert_eq!(ErrorKind::MissingTrailingSemicolon, err.kind()); - assert_eq!(err.position(), 30); - assert_eq!(err.to_string(), "missing trailing semicolon at position 30"); + let config = parse_conf_str(input).unwrap(); + assert_eq!(config.service(), "http"); + assert_eq!(config.get("host"), Some("localhost")); + assert_eq!(config.get("port"), Some("9000")); } #[test] fn escaped_semicolon_missing_trailing() { let input = "http::host=localhost;port=9000;;"; - let config = parse_conf_str(input); - assert!(config.is_err()); - let err = config.unwrap_err(); - assert_eq!(err.kind(), ErrorKind::MissingTrailingSemicolon); - assert_eq!(err.position(), 32); - assert_eq!(err.to_string(), "missing trailing semicolon at position 32"); + let config = parse_conf_str(input).unwrap(); + assert_eq!(config.service(), "http"); + assert_eq!(config.get("host"), Some("localhost")); + assert_eq!(config.get("port"), Some("9000;")); } #[test]