Skip to content
128 changes: 96 additions & 32 deletions src/de.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,51 @@
use std::str::FromStr;

use serde::de::{Visitor, IntoDeserializer};
use serde::de::value::MapDeserializer;
use serde::de::{IntoDeserializer, Visitor};

use regex::Regex;

use crate::error::*;

pub(crate) struct Deserializer<'de> {
pub struct Deserializer<'de> {
input: &'de str,
regex: Regex,
}

impl<'de> Deserializer<'de> {
pub fn new(input: &'de str, regex: Regex) -> Deserializer {
Deserializer {
input,
regex,
}
pub const fn new(input: &'de str, regex: Regex) -> Deserializer {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have a good use-case for making this const?
AFAIK creating a Regex isn't possible in a const context anyways.

Deserializer { input, regex }
}
}

impl<'de, 'a> serde::Deserializer<'de> for &'a mut Deserializer<'de> {
impl<'de> serde::Deserializer<'de> for &'_ mut Deserializer<'de> {
type Error = Error;

fn deserialize_any<V>(self, visitor: V) -> Result<V::Value> where V: Visitor<'de> {
#[inline]
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_map(visitor)
}

fn deserialize_map<V>(self, visitor: V) -> Result<V::Value> where V: Visitor<'de> {
let caps = self.regex.captures(&self.input).ok_or_else(Error::NoMatch)?;
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let caps = self.regex.captures(self.input).ok_or_else(Error::NoMatch)?;

let items = self.regex.capture_names().filter_map(|n| {
n.map(|name| {
let value = &caps[name];
(name.to_owned(), Value { name: name.to_owned(), value: value.to_owned() })
n.and_then(|name| {
caps.name(name).map(|value| {
(
name.to_owned(),
Value {
name: name.to_owned(),
value: value.as_str().to_owned(),
},
)
})
})
});

Expand All @@ -54,14 +65,17 @@ impl<'de, 'a> serde::Deserializer<'de> for &'a mut Deserializer<'de> {
}
}


struct Value {
name: String,
value: String,
}

impl Value {
fn parse<T>(&self) -> Result<T> where T: FromStr {
#[allow(clippy::map_err_ignore)]

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to not ignore this error and make the parser error available in the caller instead of ignoring this problem...

fn parse<T>(&self) -> Result<T>
where
T: FromStr,
{
self.value.parse().map_err(|_| self.get_parse_error())
}

Expand All @@ -84,11 +98,17 @@ impl<'de> IntoDeserializer<'de, Error> for Value {
impl<'de> serde::Deserializer<'de> for Value {
type Error = Error;

fn deserialize_any<V>(self, visitor: V) -> Result<V::Value> where V: Visitor<'de> {
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.value.into_deserializer().deserialize_any(visitor)
}

fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value> where V: Visitor<'de> {
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
if self.value.eq_ignore_ascii_case("true") {
visitor.visit_bool(true)
} else if self.value.eq_ignore_ascii_case("false") {
Expand All @@ -98,59 +118,103 @@ impl<'de> serde::Deserializer<'de> for Value {
}
}

fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value> where V: Visitor<'de> {
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_i8(self.parse()?)
}

fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value> where V: Visitor<'de> {
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_i16(self.parse()?)
}

fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value> where V: Visitor<'de> {
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_i32(self.parse()?)
}

fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value> where V: Visitor<'de> {
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_i64(self.parse()?)
}

fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value> where V: Visitor<'de> {
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_u8(self.parse()?)
}

fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value> where V: Visitor<'de> {
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_u16(self.parse()?)
}

fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value> where V: Visitor<'de> {
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_u32(self.parse()?)
}

fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value> where V: Visitor<'de> {
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_u64(self.parse()?)
}

fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value> where V: Visitor<'de> {
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_f32(self.parse()?)
}

fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value> where V: Visitor<'de> {
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_f64(self.parse()?)
}

fn deserialize_option<V>(self, visitor: V) -> Result<V::Value> where V: Visitor<'de> {
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
if self.value.is_empty() {
visitor.visit_none()
} else {
visitor.visit_some(self)
}
}

fn deserialize_newtype_struct<V>(self, _: &'static str, visitor: V) -> Result<V::Value> where V: Visitor<'de> {
fn deserialize_newtype_struct<V>(self, _: &'static str, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}

fn deserialize_enum<V>(self, _name: &'static str, _variants: &'static [&'static str], visitor: V) -> Result<V::Value> where V: Visitor<'de> {
fn deserialize_enum<V>(
self,
_name: &'static str,
_variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_enum(self.value.into_deserializer())
}

Expand All @@ -160,4 +224,4 @@ impl<'de> serde::Deserializer<'de> for Value {
unit seq bytes byte_buf map unit_struct
tuple_struct tuple ignored_any struct
}
}
}
23 changes: 17 additions & 6 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::fmt::{Display, Formatter};

/// An error that occurred during deserialization.
#[derive(Debug)]
#[non_exhaustive]

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think we should do this. Marking an error enum as non exhaustive makes some devs more lazy about error handling and there should be a good reason for doing this. The error handling of this crate is already quite weak and in would be better to improve this instead of allowing users to ignore errors.

pub enum Error {
/// An error occurred while parsing the regular expression
BadRegex(regex::Error),
Expand All @@ -23,25 +24,35 @@ pub enum Error {
}

impl serde::de::Error for Error {
fn custom<T>(msg: T) -> Self where T: Display {
#[inline]
fn custom<T>(msg: T) -> Self
where
T: Display,
{
Self::Custom(msg.to_string())
}
}

impl std::error::Error for Error {}

impl Display for Error {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
use Error::*;
match self {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This make the code more difficult to read without any benefit.

BadRegex(err) => err.fmt(f),
match *self {
BadRegex(ref err) => err.fmt(f),
NoMatch() => write!(f, "String doesn't match pattern"),
BadValue { name, value } => write!(f, "Unable to convert value for group {}: {}", name, value),
Custom(err) => write!(f, "{}", err),
BadValue {
ref name,
ref value,
} => {
write!(f, "Unable to convert value for group {}: {}", name, value)
}
Custom(ref err) => write!(f, "{}", err),
}
}
}

// Do not use this alias in public parts of the crate because
// it would hide the direct link to the actual error type in rustdoc.
pub(crate) type Result<T> = std::result::Result<T, Error>;
pub type Result<T> = std::result::Result<T, Error>;
Loading