// Copyright 2022 The ChromiumOS Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use std::borrow::Cow; use std::fmt; use std::fmt::Debug; use std::fmt::Display; use std::num::ParseIntError; use nom::branch::alt; use nom::bytes::complete::escaped_transform; use nom::bytes::complete::is_not; use nom::bytes::complete::tag; use nom::bytes::complete::take_while; use nom::bytes::complete::take_while1; use nom::character::complete::alphanumeric1; use nom::character::complete::anychar; use nom::character::complete::char; use nom::character::complete::none_of; use nom::combinator::map; use nom::combinator::map_res; use nom::combinator::opt; use nom::combinator::peek; use nom::combinator::recognize; use nom::combinator::value; use nom::combinator::verify; use nom::sequence::delimited; use nom::sequence::pair; use nom::sequence::tuple; use nom::AsChar; use nom::Finish; use nom::IResult; use num_traits::Num; use remain::sorted; use serde::de; use serde::Deserialize; use serde::Deserializer; use thiserror::Error; #[derive(Debug, Error, PartialEq, Eq)] #[sorted] #[non_exhaustive] #[allow(missing_docs)] /// Different kinds of errors that can be returned by the parser. pub enum ErrorKind { #[error("unexpected end of input")] Eof, #[error("expected a boolean")] ExpectedBoolean, #[error("expected ']'")] ExpectedCloseBracket, #[error("expected ','")] ExpectedComma, #[error("expected '='")] ExpectedEqual, #[error("expected an identifier")] ExpectedIdentifier, #[error("expected '['")] ExpectedOpenBracket, #[error("expected a string")] ExpectedString, #[error("\" and ' can only be used in quoted strings")] InvalidCharInString, #[error("invalid characters for number or number does not fit into its destination type")] InvalidNumber, #[error("serde error: {0}")] SerdeError(String), #[error("remaining characters in input")] TrailingCharacters, } /// Error that may be thown while parsing a key-values string. #[derive(Debug, Error, PartialEq, Eq)] pub struct ParseError { /// Detailed error that occurred. pub kind: ErrorKind, /// Index of the error in the input string. pub pos: usize, } impl Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.kind { ErrorKind::SerdeError(s) => write!(f, "{}", s), _ => write!(f, "{} at position {}", self.kind, self.pos), } } } impl de::Error for ParseError { fn custom(msg: T) -> Self where T: fmt::Display, { Self { kind: ErrorKind::SerdeError(msg.to_string()), pos: 0, } } } type Result = std::result::Result; /// Returns `true` if `c` is a valid separator character. fn is_separator(c: Option) -> bool { matches!(c, Some(',') | Some(']') | None) } /// Nom parser for valid separators. fn any_separator(s: &str) -> IResult<&str, Option> { let next_char = s.chars().next(); if is_separator(next_char) { let pos = if let Some(c) = next_char { c.len_utf8() } else { 0 }; Ok((&s[pos..], next_char)) } else { Err(nom::Err::Error(nom::error::Error::new( s, nom::error::ErrorKind::Char, ))) } } /// Nom parser for valid strings. /// /// A string can be quoted (using single or double quotes) or not. If it is not quoted, the string /// is assumed to continue until the next ',', '[', or ']' character. If it is escaped, it continues /// until the next non-escaped quote. /// /// The returned value is a slice into the current input if no characters to unescape were met, /// or a fully owned string if we had to unescape some characters. fn any_string(s: &str) -> IResult<&str, Cow> { // Double-quoted strings may escape " and \ characters. Since escaped strings are modified, // we need to return an owned `String` instead of just a slice in the input string. let double_quoted = delimited( char('"'), alt(( map( escaped_transform( none_of(r#"\""#), '\\', alt((value("\"", char('"')), value("\\", char('\\')))), ), Cow::Owned, ), map(tag(""), Cow::Borrowed), )), char('"'), ); // Single-quoted strings do not escape characters. let single_quoted = map( delimited(char('\''), alt((is_not(r#"'"#), tag(""))), char('\'')), Cow::Borrowed, ); // Unquoted strings end with the next comma or bracket and may not contain a quote or bracket // character or be empty. let unquoted = map( take_while1(|c: char| c != ',' && c != '"' && c != '\'' && c != '[' && c != ']'), Cow::Borrowed, ); alt((double_quoted, single_quoted, unquoted))(s) } /// Nom parser for valid positive of negative numbers. /// /// Hexadecimal, octal, and binary values can be specified with the `0x`, `0o` and `0b` prefixes. fn any_number(s: &str) -> IResult<&str, T> where T: Num, { // Parses the number input and returns a tuple including the number itself (with its sign) and // its radix. // // We move this non-generic part into its own function so it doesn't get monomorphized, which // would increase the binary size more than needed. fn parse_number(s: &str) -> IResult<&str, (Cow, u32)> { // Recognizes the sign prefix. let sign = char('-'); // Recognizes the radix prefix. let radix = alt(( value(16, tag("0x")), value(8, tag("0o")), value(2, tag("0b")), )); // Recognizes the trailing separator but do not consume it. let separator = peek(any_separator); // Chain of parsers: sign (optional) and radix (optional), then sequence of alphanumerical // characters. // // Then we take all 3 recognized elements and turn them into the string and radix to pass to // `from_str_radix`. map( tuple((opt(sign), opt(radix), alphanumeric1, separator)), |(sign, radix, number, _)| { // If the sign was specified, we need to build a string that contains it for // `from_str_radix` to parse the number accurately. Otherwise, simply borrow the // remainder of the input. let num_string = if let Some(sign) = sign { Cow::Owned(sign.to_string() + number) } else { Cow::Borrowed(number) }; (num_string, radix.unwrap_or(10)) }, )(s) } map_res(parse_number, |(num_string, radix)| { T::from_str_radix(&num_string, radix) })(s) } /// Nom parser for booleans. fn any_bool(s: &str) -> IResult<&str, bool> { let mut boolean = alt((value(true, tag("true")), value(false, tag("false")))); boolean(s) } /// Nom parser for identifiers. An identifier may contain any alphanumeric character, as well as /// '_' and '-' at any place excepted the first one which cannot be '-'. /// /// Usually identifiers are not allowed to start with a number, but we chose to allow this /// here otherwise options like "mode=2d" won't parse if "2d" is an alias for an enum variant. fn any_identifier(s: &str) -> IResult<&str, &str> { let mut ident = recognize(pair( verify(anychar, |&c| c.is_alphanum() || c == '_'), take_while(|c: char| c.is_alphanum() || c == '_' || c == '-'), )); ident(s) } /// Serde deserializer for key-values strings. pub struct KeyValueDeserializer<'de> { /// Full input originally received for parsing. original_input: &'de str, /// Input currently remaining to parse. input: &'de str, /// If set, then `deserialize_identifier` will take and return its content the next time it is /// called instead of trying to parse an identifier from the input. This is needed to allow the /// name of the first field of a struct to be omitted, e.g. /// /// --block "/path/to/disk.img,ro=true" /// /// instead of /// /// --block "path=/path/to/disk.img,ro=true" next_identifier: Option<&'de str>, /// Whether the '=' sign has been parsed after a key. The absence of '=' is only valid for /// boolean fields, in which case the field's value will be `true`. has_equal: bool, /// Whether the top structure has been parsed yet or not. The top structure is the only one /// that does not require to be enclosed within braces. top_struct_parsed: bool, } impl<'de> From<&'de str> for KeyValueDeserializer<'de> { fn from(input: &'de str) -> Self { Self { original_input: input, input, next_identifier: None, has_equal: false, top_struct_parsed: false, } } } impl<'de> KeyValueDeserializer<'de> { /// Return an `kind` error for the current position of the input. pub fn error_here(&self, kind: ErrorKind) -> ParseError { ParseError { kind, pos: self.original_input.len() - self.input.len(), } } /// Returns the next char in the input string without consuming it, or None /// if we reached the end of input. pub fn peek_char(&self) -> Option { self.input.chars().next() } /// Skip the next char in the input string. pub fn skip_char(&mut self) { let _ = self.next_char(); } /// Returns the next char in the input string and consume it, or returns /// None if we reached the end of input. pub fn next_char(&mut self) -> Option { let c = self.peek_char()?; self.input = &self.input[c.len_utf8()..]; Some(c) } /// Confirm that we have a separator (i.e. ',' or ']') character or have reached the end of the /// input string. fn confirm_separator(&mut self) -> Result<()> { // We must have a comma or end of input after a value. match self.peek_char() { Some(',') => { let _ = self.next_char(); Ok(()) } Some(']') | None => Ok(()), Some(_) => Err(self.error_here(ErrorKind::ExpectedComma)), } } /// Attempts to parse an identifier, either for a key or for the value of an enum type. pub fn parse_identifier(&mut self) -> Result<&'de str> { let (remainder, res) = any_identifier(self.input) .finish() .map_err(|_| self.error_here(ErrorKind::ExpectedIdentifier))?; self.input = remainder; Ok(res) } /// Attempts to parse a string. pub fn parse_string(&mut self) -> Result> { let (remainder, res) = any_string(self.input) .finish() .map_err(|e: nom::error::Error<_>| { self.input = e.input; // Any error means we did not have a well-formed string. self.error_here(ErrorKind::ExpectedString) })?; self.input = remainder; // The character following a string will be either a comma, a closing bracket, or EOS. If // we have something else, this means an unquoted string should probably have been quoted. if is_separator(self.peek_char()) { Ok(res) } else { Err(self.error_here(ErrorKind::InvalidCharInString)) } } /// Attempt to parse a boolean. pub fn parse_bool(&mut self) -> Result { let (remainder, res) = any_bool(self.input) .finish() .map_err(|e: nom::error::Error<_>| { self.input = e.input; self.error_here(ErrorKind::ExpectedBoolean) })?; self.input = remainder; Ok(res) } /// Attempt to parse a positive or negative number. pub fn parse_number(&mut self) -> Result where T: Num, { let (remainder, val) = any_number(self.input) .finish() .map_err(|_| self.error_here(ErrorKind::InvalidNumber))?; self.input = remainder; Ok(val) } /// Consume this deserializer and return a `TrailingCharacters` error if some input was /// remaining. /// /// This is useful to confirm that the whole input has been consumed without any extra elements. pub fn finish(self) -> Result<()> { if self.input.is_empty() { Ok(()) } else { Err(self.error_here(ErrorKind::TrailingCharacters)) } } } impl<'de> de::MapAccess<'de> for KeyValueDeserializer<'de> { type Error = ParseError; fn next_key_seed(&mut self, seed: K) -> Result> where K: de::DeserializeSeed<'de>, { // Detect end of input or struct. match self.peek_char() { None | Some(']') => return Ok(None), _ => (), } self.has_equal = false; let had_implicit_identifier = self.next_identifier.is_some(); let val = seed.deserialize(&mut *self).map(Some)?; // We just "deserialized" the content of `next_identifier`, so there should be no equal // character in the input. We can return now. if had_implicit_identifier { self.has_equal = true; return Ok(val); } match self.peek_char() { // We expect an equal after an identifier. Some('=') => { self.skip_char(); self.has_equal = true; Ok(val) } // Ok if we are parsing a boolean where an empty value means true. c if is_separator(c) => Ok(val), _ => Err(self.error_here(ErrorKind::ExpectedEqual)), } } fn next_value_seed(&mut self, seed: V) -> Result where V: de::DeserializeSeed<'de>, { let val = seed.deserialize(&mut *self)?; self.confirm_separator()?; Ok(val) } } /// `MapAccess` for a map with no members specified. /// /// This is used to allow a struct enum type to be specified without `[` and `]`, in which case /// all its members will take their default value: /// /// ``` /// # use serde_keyvalue::from_key_values; /// # use serde::Deserialize; /// #[derive(Deserialize, PartialEq, Eq, Debug)] /// #[serde(rename_all = "kebab-case")] /// enum FlipMode { /// Active { /// #[serde(default)] /// switch1: bool, /// #[serde(default)] /// switch2: bool, /// }, /// } /// #[derive(Deserialize, PartialEq, Eq, Debug)] /// struct TestStruct { /// mode: FlipMode, /// } /// let res: TestStruct = from_key_values("mode=active").unwrap(); /// assert_eq!( /// res, /// TestStruct { /// mode: FlipMode::Active { /// switch1: false, /// switch2: false /// } /// } /// ); /// ``` struct EmptyMapAccess; impl<'de> de::MapAccess<'de> for EmptyMapAccess { type Error = ParseError; fn next_key_seed(&mut self, _seed: K) -> Result> where K: de::DeserializeSeed<'de>, { Ok(None) } fn next_value_seed(&mut self, _seed: V) -> Result where V: de::DeserializeSeed<'de>, { // Never reached because `next_key_seed` never returns a valid key. unreachable!() } } impl<'a, 'de> de::EnumAccess<'de> for &'a mut KeyValueDeserializer<'de> { type Error = ParseError; type Variant = Self; fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant)> where V: de::DeserializeSeed<'de>, { let val = seed.deserialize(&mut *self)?; Ok((val, self)) } } impl<'a, 'de> de::VariantAccess<'de> for &'a mut KeyValueDeserializer<'de> { type Error = ParseError; fn unit_variant(self) -> Result<()> { Ok(()) } fn newtype_variant_seed(self, _seed: T) -> Result where T: de::DeserializeSeed<'de>, { unimplemented!() } fn tuple_variant(self, len: usize, visitor: V) -> Result where V: de::Visitor<'de>, { self.deserialize_tuple(len, visitor) } fn struct_variant(self, _fields: &'static [&'static str], visitor: V) -> Result where V: de::Visitor<'de>, { if self.peek_char() == Some('[') { self.next_char(); let val = self.deserialize_map(visitor)?; if self.peek_char() != Some(']') { Err(self.error_here(ErrorKind::ExpectedCloseBracket)) } else { self.next_char(); Ok(val) } } else { // The `EmptyMapAccess` failing to parse means that this enum must take arguments, i.e. // that an opening bracket is expected. visitor .visit_map(EmptyMapAccess) .map_err(|_| self.error_here(ErrorKind::ExpectedOpenBracket)) } } } impl<'de> de::SeqAccess<'de> for KeyValueDeserializer<'de> { type Error = ParseError; fn next_element_seed(&mut self, seed: T) -> Result> where T: de::DeserializeSeed<'de>, { if self.peek_char() == Some(']') { return Ok(None); } let value = seed.deserialize(&mut *self)?; self.confirm_separator()?; Ok(Some(value)) } } impl<'de, 'a> de::Deserializer<'de> for &'a mut KeyValueDeserializer<'de> { type Error = ParseError; fn deserialize_any(self, visitor: V) -> Result where V: serde::de::Visitor<'de>, { match self.peek_char() { // If we have no value following, then we are dealing with a boolean flag. c if is_separator(c) => return self.deserialize_bool(visitor), // Opening bracket means we have a sequence. Some('[') => return self.deserialize_seq(visitor), _ => (), } // This is ambiguous as technically any argument could be an unquoted string. However we // don't have any type information here, so try to guess it on a best-effort basis... if any_number::(self.input).is_ok() { self.deserialize_i64(visitor) } else if any_number::(self.input).is_ok() { self.deserialize_u64(visitor) } else if any_bool(self.input).is_ok() { self.deserialize_bool(visitor) } else { self.deserialize_str(visitor) } } fn deserialize_bool(self, visitor: V) -> Result where V: serde::de::Visitor<'de>, { // It is valid to just mention a bool as a flag and not specify its value - in this case // the value is set as `true`. let val = if self.has_equal { self.parse_bool()? } else { true }; visitor.visit_bool(val) } fn deserialize_i8(self, visitor: V) -> Result where V: serde::de::Visitor<'de>, { visitor.visit_i8(self.parse_number()?) } fn deserialize_i16(self, visitor: V) -> Result where V: serde::de::Visitor<'de>, { visitor.visit_i16(self.parse_number()?) } fn deserialize_i32(self, visitor: V) -> Result where V: serde::de::Visitor<'de>, { visitor.visit_i32(self.parse_number()?) } fn deserialize_i64(self, visitor: V) -> Result where V: serde::de::Visitor<'de>, { visitor.visit_i64(self.parse_number()?) } fn deserialize_u8(self, visitor: V) -> Result where V: serde::de::Visitor<'de>, { visitor.visit_u8(self.parse_number()?) } fn deserialize_u16(self, visitor: V) -> Result where V: serde::de::Visitor<'de>, { visitor.visit_u16(self.parse_number()?) } fn deserialize_u32(self, visitor: V) -> Result where V: serde::de::Visitor<'de>, { visitor.visit_u32(self.parse_number()?) } fn deserialize_u64(self, visitor: V) -> Result where V: serde::de::Visitor<'de>, { visitor.visit_u64(self.parse_number()?) } fn deserialize_f32(self, _visitor: V) -> Result where V: serde::de::Visitor<'de>, { unimplemented!() } fn deserialize_f64(self, _visitor: V) -> Result where V: serde::de::Visitor<'de>, { unimplemented!() } fn deserialize_char(self, visitor: V) -> Result where V: serde::de::Visitor<'de>, { visitor.visit_char( self.next_char() .ok_or_else(|| self.error_here(ErrorKind::Eof))?, ) } fn deserialize_str(self, visitor: V) -> Result where V: serde::de::Visitor<'de>, { match self.parse_string()? { Cow::Borrowed(s) => visitor.visit_borrowed_str(s), Cow::Owned(s) => visitor.visit_string(s), } } fn deserialize_string(self, visitor: V) -> Result where V: serde::de::Visitor<'de>, { self.deserialize_str(visitor) } fn deserialize_bytes(self, _visitor: V) -> Result where V: serde::de::Visitor<'de>, { unimplemented!() } fn deserialize_byte_buf(self, visitor: V) -> Result where V: serde::de::Visitor<'de>, { self.deserialize_bytes(visitor) } fn deserialize_option(self, visitor: V) -> Result where V: serde::de::Visitor<'de>, { // The fact that an option is specified implies that is exists, hence we always visit // Some() here. visitor.visit_some(self) } fn deserialize_unit(self, visitor: V) -> Result where V: serde::de::Visitor<'de>, { visitor.visit_unit() } fn deserialize_unit_struct(self, _name: &'static str, visitor: V) -> Result where V: serde::de::Visitor<'de>, { self.deserialize_unit(visitor) } fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result where V: serde::de::Visitor<'de>, { visitor.visit_newtype_struct(self) } fn deserialize_seq(self, visitor: V) -> Result where V: serde::de::Visitor<'de>, { if self.peek_char() == Some('[') { self.next_char(); let val = visitor.visit_seq(&mut *self)?; if self.peek_char() != Some(']') { Err(self.error_here(ErrorKind::ExpectedCloseBracket)) } else { self.next_char(); Ok(val) } } else { // The `EmptyMapAccess` failing to parse means that this sequence must take arguments, // i.e. that an opening bracket is expected. visitor .visit_map(EmptyMapAccess) .map_err(|_| self.error_here(ErrorKind::ExpectedOpenBracket)) } } fn deserialize_tuple(self, _len: usize, visitor: V) -> Result where V: serde::de::Visitor<'de>, { self.deserialize_seq(visitor) } fn deserialize_tuple_struct( self, _name: &'static str, _len: usize, _visitor: V, ) -> Result where V: serde::de::Visitor<'de>, { unimplemented!() } fn deserialize_map(self, visitor: V) -> Result where V: serde::de::Visitor<'de>, { // The top structure (i.e. the first structure that we will ever parse) does not need to be // enclosed in braces, but inner structures do. // // We need to do this here as well as in `deserialize_struct` because the top-element of // flattened structs will be a map, not a struct. self.top_struct_parsed = true; visitor.visit_map(self) } fn deserialize_struct( self, _name: &'static str, fields: &'static [&'static str], visitor: V, ) -> Result where V: serde::de::Visitor<'de>, { // The top structure (i.e. the first structure that we will ever parse) does not need to be // enclosed in braces, but inner structures do. let top_struct_parsed = std::mem::replace(&mut self.top_struct_parsed, true); if top_struct_parsed { if self.peek_char() == Some('[') { self.next_char(); } else { // The `EmptyMapAccess` failing to parse means that this struct must take // arguments, i.e. that an opening bracket is expected. return visitor .visit_map(EmptyMapAccess) .map_err(|_| self.error_here(ErrorKind::ExpectedOpenBracket)); } } // The name of the first field of a struct can be omitted (see documentation of // `next_identifier` for details). // // To detect this, peek the next identifier, and check if the character following is '='. If // it is not, then we may have a value in first position, unless the value is identical to // one of the field's name - in this case, assume this is a boolean using the flag syntax. self.next_identifier = match any_identifier(self.input) { Ok((_, s)) => match self.input.chars().nth(s.chars().count()) { Some('=') => None, _ => { if fields.contains(&s) { None } else { fields.first().copied() } } }, // Not an identifier, probably means this is a value for the first field then. Err(_) => fields.first().copied(), }; let ret = visitor.visit_map(&mut *self)?; if top_struct_parsed { if self.peek_char() == Some(']') { self.next_char(); } else { return Err(self.error_here(ErrorKind::ExpectedCloseBracket)); } } Ok(ret) } fn deserialize_enum( self, _name: &'static str, _variants: &'static [&'static str], visitor: V, ) -> Result where V: serde::de::Visitor<'de>, { visitor.visit_enum(self) } fn deserialize_identifier(self, visitor: V) -> Result where V: serde::de::Visitor<'de>, { let identifier = self .next_identifier .take() .map_or_else(|| self.parse_identifier(), Ok)?; visitor.visit_borrowed_str(identifier) } fn deserialize_ignored_any(self, visitor: V) -> Result where V: serde::de::Visitor<'de>, { self.deserialize_any(visitor) } } /// Attempts to deserialize `T` from the key-values string `input`. pub fn from_key_values<'a, T>(input: &'a str) -> Result where T: Deserialize<'a>, { let mut deserializer = KeyValueDeserializer::from(input); let ret = T::deserialize(&mut deserializer)?; deserializer.finish()?; Ok(ret) } #[cfg(test)] mod tests { use std::collections::BTreeSet; use std::path::PathBuf; use super::*; #[derive(Deserialize, PartialEq, Debug)] struct SingleStruct { m: T, } #[test] fn nom_any_separator() { let test_str = ",foo"; assert_eq!(any_separator(test_str), Ok((&test_str[1..], Some(',')))); let test_str = "]bar"; assert_eq!(any_separator(test_str), Ok((&test_str[1..], Some(']')))); let test_str = ""; assert_eq!(any_separator(test_str), Ok((test_str, None))); let test_str = "something,anything"; assert_eq!( any_separator(test_str), Err(nom::Err::Error(nom::error::Error::new( test_str, nom::error::ErrorKind::Char ))) ); } #[test] fn deserialize_number() { let res = from_key_values::>("m=54").unwrap(); assert_eq!(res.m, 54); let res = from_key_values::>("m=-54").unwrap(); assert_eq!(res.m, -54); // Parsing a signed into an unsigned? let res = from_key_values::>("m=-54").unwrap_err(); assert_eq!( res, ParseError { kind: ErrorKind::InvalidNumber, pos: 2 } ); // Value too big for a signed? let val = i32::MAX as u32 + 1; let res = from_key_values::>(&format!("m={}", val)).unwrap_err(); assert_eq!( res, ParseError { kind: ErrorKind::InvalidNumber, pos: 2 } ); // Not a number. let res = from_key_values::>("m=test").unwrap_err(); assert_eq!( res, ParseError { kind: ErrorKind::InvalidNumber, pos: 2, } ); // Parsing hex values let res: SingleStruct = from_key_values::>("m=0x1234abcd").unwrap(); assert_eq!(res.m, 0x1234abcd); let res: SingleStruct = from_key_values::>("m=-0x1234abcd").unwrap(); assert_eq!(res.m, -0x1234abcd); // Hex value outside range let res: ParseError = from_key_values::>("m=0xg").unwrap_err(); assert_eq!( res, ParseError { kind: ErrorKind::InvalidNumber, pos: 2, } ); // Parsing octal values let res: SingleStruct = from_key_values::>("m=0o755").unwrap(); assert_eq!(res.m, 0o755); let res: SingleStruct = from_key_values::>("m=-0o755").unwrap(); assert_eq!(res.m, -0o755); // Octal value outside range let res: ParseError = from_key_values::>("m=0o8").unwrap_err(); assert_eq!( res, ParseError { kind: ErrorKind::InvalidNumber, pos: 2, } ); // Parsing binary values let res: SingleStruct = from_key_values::>("m=0b1100").unwrap(); assert_eq!(res.m, 0b1100); let res: SingleStruct = from_key_values::>("m=-0b1100").unwrap(); assert_eq!(res.m, -0b1100); // Binary value outside range let res: ParseError = from_key_values::>("m=0b2").unwrap_err(); assert_eq!( res, ParseError { kind: ErrorKind::InvalidNumber, pos: 2, } ); } #[test] fn deserialize_string() { let kv = "m=John"; let res = from_key_values::>(kv).unwrap(); assert_eq!(res.m, "John".to_string()); // Spaces are valid (but not recommended) in unquoted strings. let kv = "m=John Doe"; let res = from_key_values::>(kv).unwrap(); assert_eq!(res.m, "John Doe".to_string()); // Empty string is not valid if unquoted let kv = "m="; let err = from_key_values::>(kv).unwrap_err(); assert_eq!( err, ParseError { kind: ErrorKind::ExpectedString, pos: 2 } ); // Quoted strings. let kv = r#"m="John Doe""#; let res = from_key_values::>(kv).unwrap(); assert_eq!(res.m, "John Doe".to_string()); let kv = r#"m='John Doe'"#; let res = from_key_values::>(kv).unwrap(); assert_eq!(res.m, "John Doe".to_string()); // Empty quoted strings. let kv = r#"m="""#; let res = from_key_values::>(kv).unwrap(); assert_eq!(res.m, "".to_string()); let kv = r#"m=''"#; let res = from_key_values::>(kv).unwrap(); assert_eq!(res.m, "".to_string()); // "=", ",", "[", "]" and "'" in quote. let kv = r#"m="val = [10, 20, 'a']""#; let res = from_key_values::>(kv).unwrap(); assert_eq!(res.m, r#"val = [10, 20, 'a']"#.to_string()); // Quotes in unquoted strings are forbidden. let kv = r#"m=val="a""#; let err = from_key_values::>(kv).unwrap_err(); assert_eq!( err, ParseError { kind: ErrorKind::InvalidCharInString, pos: 6 } ); let kv = r#"m=val='a'"#; let err = from_key_values::>(kv).unwrap_err(); assert_eq!( err, ParseError { kind: ErrorKind::InvalidCharInString, pos: 6 } ); // Brackets in unquoted strings are forbidden. let kv = r#"m=val=[a]"#; let err = from_key_values::>(kv).unwrap_err(); assert_eq!( err, ParseError { kind: ErrorKind::InvalidCharInString, pos: 6 } ); // Numbers and booleans are technically valid strings. let kv = "m=10"; let res = from_key_values::>(kv).unwrap(); assert_eq!(res.m, "10".to_string()); let kv = "m=false"; let res = from_key_values::>(kv).unwrap(); assert_eq!(res.m, "false".to_string()); // Escaped quote. let kv = r#"m="Escaped \" quote""#; let res = from_key_values::>(kv).unwrap(); assert_eq!(res.m, r#"Escaped " quote"#.to_string()); // Escaped slash at end of string. let kv = r#"m="Escaped slash\\""#; let res = from_key_values::>(kv).unwrap(); assert_eq!(res.m, r"Escaped slash\".to_string()); // Characters within single quotes should not be escaped. let kv = r#"m='Escaped \" quote'"#; let res = from_key_values::>(kv).unwrap(); assert_eq!(res.m, r#"Escaped \" quote"#.to_string()); let kv = r"m='Escaped slash\\'"; let res = from_key_values::>(kv).unwrap(); assert_eq!(res.m, r"Escaped slash\\".to_string()); } #[test] fn deserialize_unit() { from_key_values::>("m").unwrap(); from_key_values::>("m=").unwrap(); from_key_values::>("").unwrap_err(); from_key_values::>("p").unwrap_err(); from_key_values::>("m=10").unwrap_err(); } #[test] fn deserialize_bool() { let res = from_key_values::>("m=true").unwrap(); assert!(res.m); let res = from_key_values::>("m=false").unwrap(); assert!(!res.m); let res = from_key_values::>("m").unwrap(); assert!(res.m); let res = from_key_values::>("m=10").unwrap_err(); assert_eq!( res, ParseError { kind: ErrorKind::ExpectedBoolean, pos: 2, } ); let res = from_key_values::>("m=").unwrap_err(); assert_eq!( res, ParseError { kind: ErrorKind::ExpectedBoolean, pos: 2, } ); } #[test] fn deserialize_complex_struct() { #[derive(Deserialize, PartialEq, Debug)] struct TestStruct { num: usize, path: PathBuf, enable: bool, } let kv = "num=54,path=/dev/foomatic,enable=false"; let res = from_key_values::(kv).unwrap(); assert_eq!( res, TestStruct { num: 54, path: "/dev/foomatic".into(), enable: false, } ); let kv = "num=0x54,path=/dev/foomatic,enable=false"; let res = from_key_values::(kv).unwrap(); assert_eq!( res, TestStruct { num: 0x54, path: "/dev/foomatic".into(), enable: false, } ); let kv = "enable,path=/usr/lib/libossom.so.1,num=12"; let res = from_key_values::(kv).unwrap(); assert_eq!( res, TestStruct { num: 12, path: "/usr/lib/libossom.so.1".into(), enable: true, } ); // Braces specified at top-level. let kv = "[enable,path=/usr/lib/libossom.so.1,num=12]"; assert!(from_key_values::(kv).is_err()); } #[test] fn deserialize_unknown_field() { #[derive(Deserialize, PartialEq, Debug)] #[serde(deny_unknown_fields)] struct TestStruct { num: usize, path: PathBuf, enable: bool, } let kv = "enable,path=/usr/lib/libossom.so.1,num=12,foo=bar"; assert!(from_key_values::(kv).is_err()); } #[test] fn deserialize_option() { #[derive(Deserialize, PartialEq, Debug)] struct TestStruct { num: u32, opt: Option, } let kv = "num=16,opt=12"; let res: TestStruct = from_key_values(kv).unwrap(); assert_eq!( res, TestStruct { num: 16, opt: Some(12), } ); let kv = "num=16"; let res: TestStruct = from_key_values(kv).unwrap(); assert_eq!(res, TestStruct { num: 16, opt: None }); let kv = ""; assert!(from_key_values::(kv).is_err()); } #[test] fn deserialize_optional_struct_with_default() { #[derive(Deserialize, PartialEq, Debug)] struct DefaultStruct { #[serde(default)] param: u32, } #[derive(Deserialize, PartialEq, Debug)] struct TestStruct { flag: Option, } // Specify member explicitly let kv = "flag=[param=12]"; let res: TestStruct = from_key_values(kv).unwrap(); assert_eq!( res, TestStruct { flag: Some(DefaultStruct { param: 12 }) } ); // No member specified, braces present. let kv = "flag=[]"; let res: TestStruct = from_key_values(kv).unwrap(); assert_eq!( res, TestStruct { flag: Some(DefaultStruct { param: 0 }) } ); // No member specified, no braces. let kv = "flag="; let res: TestStruct = from_key_values(kv).unwrap(); assert_eq!( res, TestStruct { flag: Some(DefaultStruct { param: 0 }) } ); // No member specified, no braces, no equal sign. let kv = "flag"; let res: TestStruct = from_key_values(kv).unwrap(); assert_eq!( res, TestStruct { flag: Some(DefaultStruct { param: 0 }) } ); // No closing brace. let kv = "flag=["; assert!(from_key_values::(kv).is_err()); // No opening brace. let kv = "flag=]"; assert!(from_key_values::(kv).is_err()); } #[test] fn deserialize_optional_struct_within_flattened() { #[derive(Deserialize, PartialEq, Debug)] struct FlatStruct { a: u32, #[serde(default)] b: String, } #[derive(Deserialize, PartialEq, Debug)] struct DefaultStruct { #[serde(default)] param: u32, } #[derive(Deserialize, PartialEq, Debug)] struct TestStruct { #[serde(flatten)] flat: FlatStruct, flag: Option, } // Everything specified. let kv = "a=10,b=foomatic,flag=[param=24]"; let res: TestStruct = from_key_values(kv).unwrap(); assert_eq!( res, TestStruct { flat: FlatStruct { a: 10, b: "foomatic".into(), }, flag: Some(DefaultStruct { param: 24 }) } ); // Flag left to default value. let kv = "a=10,b=foomatic,flag"; let res: TestStruct = from_key_values(kv).unwrap(); assert_eq!( res, TestStruct { flat: FlatStruct { a: 10, b: "foomatic".into(), }, flag: Some(DefaultStruct { param: 0 }) } ); // Flattened default value unspecified. let kv = "a=10,flag=[param=24]"; let res: TestStruct = from_key_values(kv).unwrap(); assert_eq!( res, TestStruct { flat: FlatStruct { a: 10, b: Default::default(), }, flag: Some(DefaultStruct { param: 24 }) } ); // No optional, no default value. let kv = "a=10"; let res: TestStruct = from_key_values(kv).unwrap(); assert_eq!( res, TestStruct { flat: FlatStruct { a: 10, b: Default::default(), }, flag: None, } ); // Required member unspecified. let kv = "b=foomatic,flag=[param=24]"; assert!(from_key_values::(kv).is_err()); // Braces specified at top-level. let kv = "[a=10,b=foomatic,flag=[param=24]]"; assert!(from_key_values::(kv).is_err()); } #[test] fn deserialize_enum() { #[derive(Deserialize, PartialEq, Debug)] enum TestEnum { #[serde(rename = "first")] FirstVariant, #[serde(rename = "second")] SecondVariant, } let res: TestEnum = from_key_values("first").unwrap(); assert_eq!(res, TestEnum::FirstVariant,); let res: TestEnum = from_key_values("second").unwrap(); assert_eq!(res, TestEnum::SecondVariant,); from_key_values::("third").unwrap_err(); } #[test] fn deserialize_embedded_enum() { #[derive(Deserialize, PartialEq, Debug)] enum TestEnum { #[serde(rename = "first")] FirstVariant, #[serde(rename = "second")] SecondVariant, } #[derive(Deserialize, PartialEq, Debug)] struct TestStruct { variant: TestEnum, #[serde(default)] active: bool, } let res: TestStruct = from_key_values("variant=first").unwrap(); assert_eq!( res, TestStruct { variant: TestEnum::FirstVariant, active: false, } ); let res: TestStruct = from_key_values("variant=second,active=true").unwrap(); assert_eq!( res, TestStruct { variant: TestEnum::SecondVariant, active: true, } ); let res: TestStruct = from_key_values("active=true,variant=second").unwrap(); assert_eq!( res, TestStruct { variant: TestEnum::SecondVariant, active: true, } ); let res: TestStruct = from_key_values("active,variant=second").unwrap(); assert_eq!( res, TestStruct { variant: TestEnum::SecondVariant, active: true, } ); let res: TestStruct = from_key_values("active=false,variant=second").unwrap(); assert_eq!( res, TestStruct { variant: TestEnum::SecondVariant, active: false, } ); } #[test] fn deserialize_untagged_enum() { #[derive(Deserialize, PartialEq, Debug)] #[serde(untagged)] enum TestEnum { FirstVariant { first: u32 }, SecondVariant { second: bool }, } #[derive(Deserialize, PartialEq, Debug)] struct TestStruct { #[serde(flatten)] variant: TestEnum, } let res: TestStruct = from_key_values("first=10").unwrap(); assert_eq!(res.variant, TestEnum::FirstVariant { first: 10 }); let res: TestStruct = from_key_values("second=false").unwrap(); assert_eq!(res.variant, TestEnum::SecondVariant { second: false },); let res: TestStruct = from_key_values("second").unwrap(); assert_eq!(res.variant, TestEnum::SecondVariant { second: true },); from_key_values::("third=10").unwrap_err(); from_key_values::("first=some_string").unwrap_err(); from_key_values::("second=10").unwrap_err(); } #[test] fn deserialize_first_arg_string() { #[derive(Deserialize, PartialEq, Debug)] struct TestStruct { name: String, num: u8, } let res: TestStruct = from_key_values("name=foo,num=12").unwrap(); assert_eq!( res, TestStruct { name: "foo".into(), num: 12, } ); let res: TestStruct = from_key_values("foo,num=12").unwrap(); assert_eq!( res, TestStruct { name: "foo".into(), num: 12, } ); } #[test] fn deserialize_first_arg_int() { #[derive(Deserialize, PartialEq, Debug)] struct TestStruct { num: u8, name: String, } let res: TestStruct = from_key_values("name=foo,num=12").unwrap(); assert_eq!( res, TestStruct { num: 12, name: "foo".into(), } ); let res: TestStruct = from_key_values("12,name=foo").unwrap(); assert_eq!( res, TestStruct { num: 12, name: "foo".into(), } ); } #[test] fn deserialize_tuple() { #[derive(Deserialize, PartialEq, Debug)] struct TestStruct { size: (u32, u32), } let res: TestStruct = from_key_values("size=[320,200]").unwrap(); assert_eq!(res, TestStruct { size: (320, 200) }); // Unterminated tuple. let err = from_key_values::("size=[320]").unwrap_err(); assert_eq!( err, ParseError { kind: ErrorKind::SerdeError("invalid length 1, expected a tuple of size 2".into()), pos: 0, } ); // Too many elements in tuple. let err = from_key_values::("size=[320,200,255]").unwrap_err(); assert_eq!( err, ParseError { kind: ErrorKind::ExpectedCloseBracket, pos: 14, } ); // Non-closed sequence is invalid. let err = from_key_values::("size=[320,200").unwrap_err(); assert_eq!( err, ParseError { kind: ErrorKind::ExpectedCloseBracket, pos: 13, } ); } #[test] fn deserialize_vector() { #[derive(Deserialize, PartialEq, Debug)] struct TestStruct { numbers: Vec, } let res: TestStruct = from_key_values("numbers=[1,2,4,8,16,32,64]").unwrap(); assert_eq!( res, TestStruct { numbers: vec![1, 2, 4, 8, 16, 32, 64], } ); } #[test] fn deserialize_vector_of_strings() { #[derive(Deserialize, PartialEq, Debug)] struct TestStruct { strs: Vec, } // Unquoted strings let res: TestStruct = from_key_values(r#"strs=[singleword,camel_cased,kebab-cased]"#).unwrap(); assert_eq!( res, TestStruct { strs: vec![ "singleword".into(), "camel_cased".into(), "kebab-cased".into() ], } ); // All quoted strings let res: TestStruct = from_key_values(r#"strs=["first string","second string","third string"]"#).unwrap(); assert_eq!( res, TestStruct { strs: vec![ "first string".into(), "second string".into(), "third string".into() ], } ); // Mix let res: TestStruct = from_key_values(r#"strs=[unquoted,"quoted string",'quoted with escape "']"#).unwrap(); assert_eq!( res, TestStruct { strs: vec![ "unquoted".into(), "quoted string".into(), "quoted with escape \"".into() ], } ); } #[test] fn deserialize_vector_of_structs() { #[derive(Deserialize, PartialEq, Debug)] #[serde(deny_unknown_fields)] struct Display { size: (u32, u32), #[serde(default)] disabled: bool, } #[derive(Deserialize, PartialEq, Debug)] #[serde(deny_unknown_fields)] struct TestStruct { displays: Vec, hostname: Option, } let res: TestStruct = from_key_values("displays=[[size=[640,480]]]").unwrap(); assert_eq!( res, TestStruct { displays: vec![Display { size: (640, 480), disabled: false, }], hostname: None, } ); let res: TestStruct = from_key_values("hostname=crosmatic,displays=[[size=[800,600],disabled]]").unwrap(); assert_eq!( res, TestStruct { displays: vec![Display { size: (800, 600), disabled: true, }], hostname: Some("crosmatic".to_string()), } ); // First field of a struct does not need to be named even if it is not the top-level struct. let res: TestStruct = from_key_values("displays=[[[640,480]],[[800,600],disabled]]").unwrap(); assert_eq!( res, TestStruct { displays: vec![ Display { size: (640, 480), disabled: false, }, Display { size: (800, 600), disabled: true, } ], hostname: None, } ); let res: TestStruct = from_key_values("displays=[[[1024,768]],[size=[800,600],disabled]],hostname=crosmatic") .unwrap(); assert_eq!( res, TestStruct { displays: vec![ Display { size: (1024, 768), disabled: false, }, Display { size: (800, 600), disabled: true, } ], hostname: Some("crosmatic".to_string()), } ); } #[test] fn deserialize_set() { #[derive(Deserialize, PartialEq, Eq, Debug, PartialOrd, Ord)] #[serde(rename_all = "kebab-case")] enum Flags { Awesome, Fluffy, Transparent, } #[derive(Deserialize, PartialEq, Debug)] struct TestStruct { flags: BTreeSet, } let res: TestStruct = from_key_values("flags=[awesome,fluffy]").unwrap(); assert_eq!( res, TestStruct { flags: BTreeSet::from([Flags::Awesome, Flags::Fluffy]), } ); // Unknown enum variant? let err = from_key_values::("flags=[awesome,spiky]").unwrap_err(); assert_eq!( err, ParseError { kind: ErrorKind::SerdeError( "unknown variant `spiky`, expected one of `awesome`, `fluffy`, `transparent`" .into() ), pos: 0, } ); } #[test] fn deserialize_struct_and_tuple_enum() { #[derive(Deserialize, PartialEq, Debug)] #[serde(rename_all = "kebab-case")] enum VideoMode { Fullscreen, WindowAsTuple(u32, u32), WindowAsStruct { width: u32, height: u32 }, } #[derive(Deserialize, PartialEq, Debug)] struct TestStruct { mode: VideoMode, } let res: TestStruct = from_key_values("mode=fullscreen").unwrap(); assert_eq!( res, TestStruct { mode: VideoMode::Fullscreen } ); let res: TestStruct = from_key_values("mode=window-as-tuple[640,480]").unwrap(); assert_eq!( res, TestStruct { mode: VideoMode::WindowAsTuple(640, 480), } ); // Missing values let err = from_key_values::("mode=window-as-tuple").unwrap_err(); assert_eq!( err, ParseError { kind: ErrorKind::ExpectedOpenBracket, pos: 20, } ); let res: TestStruct = from_key_values("mode=window-as-struct[width=800,height=600]").unwrap(); assert_eq!( res, TestStruct { mode: VideoMode::WindowAsStruct { width: 800, height: 600, } } ); // Missing values. let err = from_key_values::("mode=window-as-struct").unwrap_err(); assert_eq!( err, ParseError { kind: ErrorKind::ExpectedOpenBracket, pos: 21, } ); } #[test] fn deserialize_struct_enum_with_default() { #[derive(Deserialize, PartialEq, Debug)] #[serde(rename_all = "kebab-case")] enum FlipMode { Inactive, Active { #[serde(default)] switch1: bool, #[serde(default)] switch2: bool, }, } #[derive(Deserialize, PartialEq, Debug)] struct TestStruct { mode: FlipMode, } // Only specify one member and expect the other to be default. let res: TestStruct = from_key_values("mode=active[switch1=true]").unwrap(); assert_eq!( res, TestStruct { mode: FlipMode::Active { switch1: true, switch2: false } } ); // Specify boolean members without explicit value. let res: TestStruct = from_key_values("mode=active[switch1,switch2]").unwrap(); assert_eq!( res, TestStruct { mode: FlipMode::Active { switch1: true, switch2: true } } ); // No member specified, braces present. let res: TestStruct = from_key_values("mode=active[]").unwrap(); assert_eq!( res, TestStruct { mode: FlipMode::Active { switch1: false, switch2: false } } ); // No member specified and no braces. let res: TestStruct = from_key_values("mode=active").unwrap(); assert_eq!( res, TestStruct { mode: FlipMode::Active { switch1: false, switch2: false } } ); // Non-struct variant should be recognized without braces. let res: TestStruct = from_key_values("mode=inactive").unwrap(); assert_eq!( res, TestStruct { mode: FlipMode::Inactive, } ); // Non-struct variant should not accept braces. let err = from_key_values::("mode=inactive[]").unwrap_err(); assert_eq!( err, ParseError { kind: ErrorKind::ExpectedComma, pos: 13, } ); } }