Skip to content

Commit

Permalink
fix: fixed unexpected successful logfmt parsing of single-word input (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
pamburus authored May 5, 2024
1 parent 5b975a7 commit 57719e6
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 164 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ members = [".", "crate/encstr"]
[workspace.package]
repository = "https://github.com/pamburus/hl"
authors = ["Pavel Ivanov <[email protected]>"]
version = "0.29.2-alpha.2"
version = "0.29.2-alpha.3"
edition = "2021"
license = "MIT"

Expand Down
256 changes: 115 additions & 141 deletions src/logfmt/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{
str,
};

use serde::de::{self, DeserializeSeed, EnumAccess, IntoDeserializer, MapAccess, SeqAccess, VariantAccess, Visitor};
use serde::de::{self, DeserializeSeed, IntoDeserializer, MapAccess, SeqAccess, Visitor};
use serde::Deserialize;

use super::error::{Error, Result};
Expand Down Expand Up @@ -356,18 +356,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
where
V: Visitor<'de>,
{
if self.parser.peek() == Some(b'"') {
visitor.visit_enum(self.parse_string(false)?.into_deserializer())
} else if self.parser.next() == Some(b'{') {
let value = visitor.visit_enum(Enum::new(self))?;
if self.parser.next() == Some(b'}') {
Ok(value)
} else {
Err(Error::ExpectedMapEnd)
}
} else {
Err(Error::ExpectedEnum)
}
visitor.visit_enum(self.parse_string(false)?.into_deserializer())
}

#[inline]
Expand Down Expand Up @@ -532,7 +521,9 @@ impl<'de> Parser<'de> {
}

let s = &self.input[start..self.index];
self.next();
if self.next() != Some(b'=') {
return Err(Error::ExpectedKeyValueDelimiter);
}

if unicode {
return Ok(str::from_utf8(s).map_err(|_| Error::InvalidUnicodeCodePoint)?);
Expand Down Expand Up @@ -802,67 +793,6 @@ impl<'de, 'a> MapAccess<'de> for KeyValueSequence<'a, 'de> {
}
}

struct Enum<'a, 'de: 'a> {
de: &'a mut Deserializer<'de>,
}

impl<'a, 'de> Enum<'a, 'de> {
#[inline]
fn new(de: &'a mut Deserializer<'de>) -> Self {
Enum { de }
}
}

impl<'de, 'a> EnumAccess<'de> for Enum<'a, 'de> {
type Error = Error;
type Variant = Self;

#[inline]
fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)>
where
V: DeserializeSeed<'de>,
{
let val = seed.deserialize(&mut *self.de)?;
if self.de.parser.next() == Some(b'=') {
Ok((val, self))
} else {
Err(Error::ExpectedMapKeyValueDelimiter)
}
}
}

impl<'de, 'a> VariantAccess<'de> for Enum<'a, 'de> {
type Error = Error;

fn unit_variant(self) -> Result<()> {
Err(Error::ExpectedString)
}

#[inline]
fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value>
where
T: DeserializeSeed<'de>,
{
seed.deserialize(self.de)
}

#[inline]
fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
de::Deserializer::deserialize_seq(self.de, visitor)
}

#[inline]
fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
de::Deserializer::deserialize_map(self.de, visitor)
}
}

pub enum Reference<'b, 'c, T>
where
T: ?Sized + 'static,
Expand Down Expand Up @@ -984,73 +914,117 @@ static KEY: [u8; 256] = {

// ---

#[test]
fn test_struct_no_escape() {
#[derive(Deserialize, PartialEq, Debug)]
struct Test {
int: u32,
str1: String,
str2: String,
}

let j = r#"int=42 str1=a str2="b c""#;
let expected = Test {
int: 42,
str1: "a".to_string(),
str2: "b c".to_string(),
};
assert_eq!(expected, from_str(j).unwrap());
}
#[cfg(test)]
mod tests {
use super::{super::raw::RawValue, *};
use std::collections::HashMap;

#[test]
fn test_struct_no_escape() {
#[derive(Deserialize, PartialEq, Debug)]
struct Test {
int: u32,
str1: String,
str2: String,
}

#[test]
fn test_struct_escape() {
#[derive(Deserialize, PartialEq, Debug)]
struct Test {
int: u32,
str1: String,
str2: String,
}

let j = r#"int=0 str1="b=c" str2="a\nb""#;
let expected = Test {
int: 0,
str1: "b=c".to_string(),
str2: "a\nb".to_string(),
};
assert_eq!(expected, from_str(j).unwrap());
}
let j = r#"int=42 str1=a str2="b c""#;
let expected = Test {
int: 42,
str1: "a".to_string(),
str2: "b c".to_string(),
};
assert_eq!(expected, from_str(j).unwrap());
}

#[test]
fn test_hex_escape() {
#[derive(Deserialize, PartialEq, Debug)]
struct Test {
int: u32,
str1: String,
str2: String,
}

let j = r#"int=0 str1="\u001b[3m" str2="a""#;
let expected = Test {
int: 0,
str1: "\x1b[3m".to_string(),
str2: "a".to_string(),
};
assert_eq!(expected, from_str(j).unwrap());
}
#[test]
fn test_struct_escape() {
#[derive(Deserialize, PartialEq, Debug)]
struct Test {
int: u32,
str1: String,
str2: String,
}

let j = r#"int=0 str1="b=c" str2="a\nb""#;
let expected = Test {
int: 0,
str1: "b=c".to_string(),
str2: "a\nb".to_string(),
};
assert_eq!(expected, from_str(j).unwrap());
}

#[test]
fn test_hex_escape() {
#[derive(Deserialize, PartialEq, Debug)]
struct Test {
int: u32,
str1: String,
str2: String,
}

let j = r#"int=0 str1="\u001b[3m" str2="a""#;
let expected = Test {
int: 0,
str1: "\x1b[3m".to_string(),
str2: "a".to_string(),
};
assert_eq!(expected, from_str(j).unwrap());
}

#[test]
fn test_raw() {
#[derive(Deserialize)]
struct Test<'a> {
int: i32,
str1: String,
#[serde(borrow)]
str2: &'a RawValue,
}

let j = r#"int=-42 str1=a str2="b \nc""#;
let parsed: Test = from_str(j).unwrap();
assert_eq!(parsed.int, -42);
assert_eq!(parsed.str1, "a");
assert_eq!(parsed.str2.get(), r#""b \nc""#);
}

#[test]
fn test_single_word() {
let result = from_str::<HashMap<String, String>>(r#"word"#);
assert_eq!(result, Err(Error::ExpectedKeyValueDelimiter));
assert_eq!(result.unwrap_err().to_string(), "expected key-value delimiter");
}

#[test]
fn test_raw() {
#[derive(Deserialize)]
struct Test<'a> {
int: i32,
str1: String,
#[serde(borrow)]
str2: &'a super::raw::RawValue,
}

let j = r#"int=-42 str1=a str2="b \nc""#;
let parsed: Test = from_str(j).unwrap();
assert_eq!(parsed.int, -42);
assert_eq!(parsed.str1, "a");
assert_eq!(parsed.str2.get(), r#""b \nc""#);
#[test]
fn test_raw_enum() {
#[derive(Deserialize, PartialEq, Debug)]
enum TestEnum {
A,
B,
C,
}

let val: TestEnum = from_str("B").unwrap();
assert_eq!(val, TestEnum::B);
}

#[test]
fn test_raw_struct_with_enum() {
#[derive(Deserialize, PartialEq, Debug)]
enum TestEnum {
A,
B,
C,
}

#[derive(Deserialize, PartialEq, Debug)]
struct TestStruct {
v: TestEnum,
}

let val: TestStruct = from_str("v=B").unwrap();
assert_eq!(val, TestStruct { v: TestEnum::B });
}
}
20 changes: 3 additions & 17 deletions src/logfmt/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,15 @@ use serde::de;

pub type Result<T> = std::result::Result<T, Error>;

#[derive(Debug)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Error {
Eof,
ExpectedBoolean,
ExpectedInteger,
ExpectedNull,
ExpectedString,
ExpectedArray,
ExpectedArrayDelimiter,
ExpectedArrayEnd,
ExpectedMap,
ExpectedMapDelimiter,
ExpectedMapKeyValueDelimiter,
ExpectedMapEnd,
ExpectedEnum,
ExpectedKey,
ExpectedKeyValueDelimiter,
Syntax,
InvalidEscape,
LoneLeadingSurrogateInHexEscape,
Expand All @@ -40,15 +33,8 @@ impl fmt::Display for Error {
Self::ExpectedInteger => f.write_str("expected integer"),
Self::ExpectedNull => f.write_str("expected null"),
Self::ExpectedString => f.write_str("expected string"),
Self::ExpectedArray => f.write_str("expected array"),
Self::ExpectedArrayDelimiter => f.write_str("expected space or array end"),
Self::ExpectedArrayEnd => f.write_str("expected array end"),
Self::ExpectedMap => f.write_str("expected map"),
Self::ExpectedMapDelimiter => f.write_str("expected space or map end"),
Self::ExpectedMapKeyValueDelimiter => f.write_str("expected equal sign"),
Self::ExpectedMapEnd => f.write_str("expected map end"),
Self::ExpectedEnum => f.write_str("expected enum"),
Self::ExpectedKey => f.write_str("expected key"),
Self::ExpectedKeyValueDelimiter => f.write_str("expected key-value delimiter"),
Self::Syntax => f.write_str("syntax error"),
Self::InvalidEscape => f.write_str("invalid escape sequence"),
Self::LoneLeadingSurrogateInHexEscape => f.write_str("lone leading surrogate in hex escape"),
Expand Down
2 changes: 2 additions & 0 deletions src/logfmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ pub mod error;
pub mod raw;

pub use de::{from_slice, from_str};
#[allow(unused_imports)]
pub use error::Error;
2 changes: 1 addition & 1 deletion src/logfmt/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ impl<'de: 'a, 'a> Deserialize<'de> for &'a RawValue {
type Value = &'de RawValue;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "any valid JSON value")
write!(formatter, "any valid logfmt value")
}

fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
Expand Down
Loading

0 comments on commit 57719e6

Please sign in to comment.