Created
April 24, 2022 14:56
-
-
Save nanpuyue/472ca47e13d02fbfa2dbeea4c70d51bb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// date: 2022-04-24 | |
// license: GPLv3 https://www.gnu.org/licenses/gpl-3.0.txt | |
// author: nanpuyue <[email protected]> https://blog.nanpuyue.com | |
use std::fmt::{self, Debug, Display, Formatter}; | |
use std::iter::Peekable; | |
use std::str::Lines; | |
use std::{error, result}; | |
use serde::de::{self, Visitor}; | |
use serde::forward_to_deserialize_any; | |
use serde::Deserialize; | |
const DELIM: char = ':'; | |
type Result<T> = result::Result<T, Error>; | |
pub fn from_str<'a, T: Deserialize<'a>>(s: &'a str) -> Result<T> { | |
T::deserialize(&mut Deserializer::from_str(s)) | |
} | |
pub struct Deserializer<'de> { | |
lines: Peekable<Lines<'de>>, | |
} | |
#[derive(Debug)] | |
pub enum Error { | |
Message(String), | |
KeyNotFound, | |
ValueNotFound, | |
InvalidValue, | |
Unimplemented, | |
} | |
impl Display for Error { | |
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { | |
Debug::fmt(&self, f) | |
} | |
} | |
impl error::Error for Error {} | |
impl de::Error for Error { | |
fn custom<T: fmt::Display>(msg: T) -> Self { | |
Self::Message(msg.to_string()) | |
} | |
} | |
struct MapAccess<'a, 'de>(&'a mut Deserializer<'de>); | |
impl<'a, 'de> de::MapAccess<'de> for MapAccess<'a, 'de> { | |
type Error = Error; | |
fn next_key_seed<K: de::DeserializeSeed<'de>>(&mut self, seed: K) -> Result<Option<K::Value>> { | |
Ok(seed.deserialize(&mut *self.0).ok()) | |
} | |
fn next_value_seed<V: de::DeserializeSeed<'de>>(&mut self, seed: V) -> Result<V::Value> { | |
seed.deserialize(&mut *self.0) | |
} | |
} | |
impl<'de> Deserializer<'de> { | |
fn from_str(s: &'de str) -> Self { | |
Self { | |
lines: s.lines().peekable(), | |
} | |
} | |
fn next_key(&mut self) -> Option<&'de str> { | |
self.lines.peek().and_then(|&x| x.split(DELIM).next()) | |
} | |
fn next_value(&mut self) -> Option<&'de str> { | |
self.lines | |
.next() | |
.and_then(|x| x.split(DELIM).nth(1).map(str::trim)) | |
} | |
} | |
impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { | |
type Error = Error; | |
fn deserialize_any<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value> { | |
Err(Error::Unimplemented) | |
} | |
fn deserialize_string<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> { | |
self.deserialize_str(visitor) | |
} | |
fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> { | |
visitor.visit_some(self) | |
} | |
fn deserialize_str<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> { | |
let value = self.next_value().ok_or(Error::ValueNotFound)?; | |
visitor.visit_borrowed_str(value) | |
} | |
fn deserialize_f64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> { | |
let value = self | |
.next_value() | |
.ok_or(Error::ValueNotFound) | |
.and_then(|x| x.parse().map_err(|_| Error::InvalidValue))?; | |
visitor.visit_f64(value) | |
} | |
fn deserialize_identifier<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> { | |
let ident = self.next_key().ok_or(Error::KeyNotFound)?; | |
visitor.visit_borrowed_str(ident) | |
} | |
fn deserialize_map<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> { | |
visitor.visit_map(MapAccess(self)) | |
} | |
fn deserialize_struct<V: Visitor<'de>>( | |
self, | |
_name: &'static str, | |
_fields: &'static [&'static str], | |
visitor: V, | |
) -> Result<V::Value> { | |
self.deserialize_map(visitor) | |
} | |
forward_to_deserialize_any! { | |
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 char | |
bytes byte_buf unit unit_struct newtype_struct seq tuple | |
tuple_struct enum ignored_any | |
} | |
} | |
#[derive(Debug, PartialEq, Deserialize)] | |
#[serde(rename_all = "PascalCase")] | |
pub struct SomeStruct<'a> { | |
pub field_a: &'a str, | |
pub field_b: Option<f64>, | |
pub field_c: Option<f64>, | |
} | |
fn main() { | |
let input = "FieldA:helloworld\nFieldB:1.234567\nFieldC:7.654321\n"; | |
let data = from_str::<SomeStruct>(input).unwrap(); | |
assert_eq!( | |
dbg!(data), | |
SomeStruct { | |
field_a: "helloworld", | |
field_b: Some(1.234567), | |
field_c: Some(7.654321) | |
} | |
); | |
let input = "FieldA:helloworld\nFieldC:7.654321\n"; | |
let data = from_str::<SomeStruct>(input).unwrap(); | |
assert_eq!( | |
dbg!(data), | |
SomeStruct { | |
field_a: "helloworld", | |
field_b: None, | |
field_c: Some(7.654321) | |
} | |
); | |
let input = "FieldB:1.234567\nFieldC:7.654321\n"; | |
from_str::<SomeStruct>(input).expect_err("should be error"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment