Skip to content

Commit

Permalink
Merge pull request #150 from Grinkers/parse
Browse files Browse the repository at this point in the history
EdnError rework (new parse impl)
  • Loading branch information
naomijub authored Jun 7, 2024
2 parents c60d05b + f658a21 commit bc5bc8f
Show file tree
Hide file tree
Showing 17 changed files with 796 additions and 691 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ repository = "https://github.com/edn-rs/edn-rs"
keywords = ["EDN", "no_std"]
license = "MIT"
edition = "2021"
resolver = "2"

[lints.rust]
rust_2018_idioms = "warn"
Expand All @@ -18,6 +19,7 @@ unsafe_code = "deny"
[lints.clippy]
pedantic = "warn"
nursery = "warn"
inline_always = "allow"

[features]
default = ["sets", "std"]
Expand Down
20 changes: 17 additions & 3 deletions bb.edn
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
{:tasks
{clean {:doc "Removes target folder"
:requires ([babashka.fs :as fs])
:task (fs/delete-tree "target")}
{:init (do (def code-cov-env
{"CARGO_INCREMENTAL" "0"
"RUSTFLAGS" "-Cinstrument-coverage -Copt-level=0 -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off"
"LLVM_PROFILE_FILE" "target/coverage/cargo-test-%p-%m.profraw"}))
:requires ([babashka.fs :as fs])
clean {:doc "Removes target folder"
:task (fs/delete-tree "target")}
test_lib_features (shell "cargo test --all-features --no-fail-fast")
test_lib_no_default_features (shell "cargo test --features std --no-default-features --no-fail-fast")
example_fast (shell "cargo test --examples --no-fail-fast")
Expand All @@ -23,5 +27,15 @@
:task (shell "cargo clippy --features json --no-default-features -- -W future-incompatible -W rust_2018_idioms -W clippy::all -W clippy::pedantic -W clippy::nursery --deny warnings")}
clippy {:doc "Runs all variations of cargo clippy"
:depends [cargo-clippy-all-features cargo-clippy-no-sets-json cargo-clippy-no-defaults]}
cov-all-features {:doc "Coverage, all features"
:task (shell {:extra-env code-cov-env} "cargo test --all-features")}
cov-std-only {:doc "Coverage, std only"
:task (shell {:extra-env code-cov-env} "cargo test --no-default-features --features std")}
clean-cov {:doc "Cleans all .profraw files and generated html"
:task (fs/delete-tree "target/coverage")}
grcov {:doc "Runs grcov to generate human readable html"
:task (shell "grcov target/coverage --binary-path ./target/debug/deps/ -s . -t html --branch --ignore-not-existing -o target/coverage/html")}
coverage {:doc "Generates coverage in human friendly html in target/coverage/"
:depends [clean-cov cov-all-features cov-std-only grcov]}
test {:doc "Runs all tests and checks"
:depends [cargo-test cargo-fmt clippy]}}}
7 changes: 1 addition & 6 deletions examples/complex_struct_deserialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,7 @@ fn complex_wrong() -> Result<(), EdnError> {
let bad_edn_str = "{:list [{:name \"rose\" :age \"some text\" :cool true}, {:name \"josh\" :age 33 :cool false}, {:name \"eva\" :age 296 :cool true}]}";
let complex: Result<Complex, EdnError> = edn_rs::from_str(bad_edn_str);

assert_eq!(
complex,
Err(EdnError::Deserialize(
"couldn't convert `\"some text\"` into `uint`".to_string()
))
);
assert!(complex.is_err());

Ok(())
}
Expand Down
7 changes: 1 addition & 6 deletions examples/from_edn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,7 @@ fn person_mistyped() -> Result<(), EdnError> {
}));
let person: Result<Person, EdnError> = edn_rs::from_edn(&bad_edn);

assert_eq!(
person,
Err(EdnError::Deserialize(
"couldn't convert `\"some text\"` into `uint`".to_string()
))
);
assert!(person.is_err());

Ok(())
}
Expand Down
14 changes: 2 additions & 12 deletions examples/struct_from_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,25 +36,15 @@ fn person_mistyped() -> Result<(), EdnError> {
let bad_edn_str = "{:name \"rose\" :age \"some text\" }";
let person: Result<Person, EdnError> = edn_rs::from_str(bad_edn_str);

assert_eq!(
person,
Err(EdnError::Deserialize(
"couldn't convert `\"some text\"` into `uint`".to_string()
))
);

assert!(person.is_err());
Ok(())
}

fn person_overflow() -> Result<(), EdnError> {
let overflow_edn_str = " {:name \"rose\" :age 9002 } ";
let person: Result<Person, EdnError> = edn_rs::from_str(overflow_edn_str);

assert_eq!(
format!("{person:?}"),
"Err(TryFromInt(TryFromIntError(())))"
);

assert!(person.is_err());
Ok(())
}

Expand Down
133 changes: 46 additions & 87 deletions src/deserialize/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use alloc::collections::BTreeMap;
#[cfg(feature = "sets")]
use alloc::collections::BTreeSet;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::any;
Expand All @@ -12,7 +11,8 @@ use std::collections::HashMap;
#[cfg(all(feature = "sets", feature = "std"))]
use std::collections::HashSet;

use crate::edn::{Edn, Error};
use crate::edn::Edn;
use crate::EdnError as Error;

pub mod parse;

Expand All @@ -23,7 +23,7 @@ use ordered_float::OrderedFloat;
///
/// # Errors
///
/// Error will be like `EdnError::Deserialize("couldn't convert <value> into <type>")`
/// Error implements Debug. See docs for more information.
///
/// ```
/// use crate::edn_rs::{Edn, EdnError, Deserialize};
Expand Down Expand Up @@ -60,27 +60,22 @@ use ordered_float::OrderedFloat;
/// let bad_edn_str = "{:name \"rose\" :age \"some text\" }";
/// let person: Result<Person, EdnError> = edn_rs::from_str(bad_edn_str);
///
/// assert_eq!(
/// person,
/// Err(EdnError::Deserialize(
/// "couldn't convert `\"some text\"` into `uint`".to_string()
/// ))
/// );
/// println!("{:?}", person);
/// ```
#[allow(clippy::missing_errors_doc)]
pub trait Deserialize: Sized {
fn deserialize(edn: &Edn) -> Result<Self, Error>;
}

fn build_deserialize_error(edn: &Edn, type_: &str) -> Error {
Error::Deserialize(format!("couldn't convert `{edn}` into `{type_}`"))
const fn build_deserialize_error(type_: &'static str) -> Error {
Error::deserialize(type_)
}

impl Deserialize for () {
fn deserialize(edn: &Edn) -> Result<Self, Error> {
match edn {
Edn::Nil => Ok(()),
_ => Err(build_deserialize_error(edn, "unit")),
_ => Err(build_deserialize_error("unit")),
}
}
}
Expand All @@ -89,15 +84,15 @@ impl Deserialize for () {
impl Deserialize for OrderedFloat<f64> {
fn deserialize(edn: &Edn) -> Result<Self, Error> {
edn.to_float()
.ok_or_else(|| build_deserialize_error(edn, "edn_rs::Double"))
.ok_or_else(|| build_deserialize_error("edn_rs::Double"))
.map(Into::into)
}
}

impl Deserialize for f64 {
fn deserialize(edn: &Edn) -> Result<Self, Error> {
edn.to_float()
.ok_or_else(|| build_deserialize_error(edn, "edn_rs::Double"))
.ok_or_else(|| build_deserialize_error("edn_rs::Double"))
.map(Into::into)
}
}
Expand All @@ -109,7 +104,7 @@ macro_rules! impl_deserialize_int {
fn deserialize(edn: &Edn) -> Result<Self, Error> {
let int = edn
.to_int()
.ok_or_else(|| build_deserialize_error(edn, "int"))?;
.ok_or_else(|| build_deserialize_error("int"))?;
Ok(Self::try_from(int)?)
}
}
Expand All @@ -126,7 +121,7 @@ macro_rules! impl_deserialize_uint {
fn deserialize(edn: &Edn) -> Result<Self, Error> {
let uint = edn
.to_uint()
.ok_or_else(|| build_deserialize_error(edn, "uint"))?;
.ok_or_else(|| build_deserialize_error("uint"))?;
Ok(Self::try_from(uint)?)
}
}
Expand All @@ -138,8 +133,7 @@ impl_deserialize_uint!(u8, u16, u32, u64, usize);

impl Deserialize for bool {
fn deserialize(edn: &Edn) -> Result<Self, Error> {
edn.to_bool()
.ok_or_else(|| build_deserialize_error(edn, "bool"))
edn.to_bool().ok_or_else(|| build_deserialize_error("bool"))
}
}

Expand All @@ -160,8 +154,7 @@ impl Deserialize for String {

impl Deserialize for char {
fn deserialize(edn: &Edn) -> Result<Self, Error> {
edn.to_char()
.ok_or_else(|| build_deserialize_error(edn, "char"))
edn.to_char().ok_or_else(|| build_deserialize_error("char"))
}
}

Expand All @@ -171,23 +164,23 @@ where
{
fn deserialize(edn: &Edn) -> Result<Self, Error> {
match edn {
Edn::Vector(_) => Ok(edn
.iter_some()
.ok_or_else(|| Error::Iter(format!("Could not create iter from {edn:?}")))?
Edn::Vector(v) => Ok(v
.0
.iter()
.map(|e| Deserialize::deserialize(e))
.collect::<Result<Self, Error>>()?),
Edn::List(_) => Ok(edn
.iter_some()
.ok_or_else(|| Error::Iter(format!("Could not create iter from {edn:?}")))?
Edn::List(l) => Ok(l
.0
.iter()
.map(|e| Deserialize::deserialize(e))
.collect::<Result<Self, Error>>()?),
#[cfg(feature = "sets")]
Edn::Set(_) => Ok(edn
.iter_some()
.ok_or_else(|| Error::Iter(format!("Could not create iter from {edn:?}")))?
Edn::Set(s) => Ok(s
.0
.iter()
.map(|e| Deserialize::deserialize(e))
.collect::<Result<Self, Error>>()?),
_ => Err(build_deserialize_error(edn, any::type_name::<Self>())),
_ => Err(build_deserialize_error(any::type_name::<Self>())),
}
}
}
Expand All @@ -200,22 +193,17 @@ where
{
fn deserialize(edn: &Edn) -> Result<Self, Error> {
match edn {
Edn::Map(_) => edn
.map_iter()
.ok_or_else(|| Error::Iter(format!("Could not create iter from {edn:?}")))?
Edn::Map(m) => m
.0
.iter()
.map(|(key, e)| {
Ok((
key.to_string(),
Deserialize::deserialize(e).map_err(|_| {
Error::Deserialize(format!(
"Cannot safely deserialize {:?} to {}",
edn, "HashMap"
))
})?,
Deserialize::deserialize(e).map_err(|_| Error::deserialize("HashMap"))?,
))
})
.collect::<Result<Self, Error>>(),
_ => Err(build_deserialize_error(edn, any::type_name::<Self>())),
_ => Err(build_deserialize_error(any::type_name::<Self>())),
}
}
}
Expand All @@ -226,22 +214,17 @@ where
{
fn deserialize(edn: &Edn) -> Result<Self, Error> {
match edn {
Edn::Map(_) => edn
.map_iter()
.ok_or_else(|| Error::Iter(format!("Could not create iter from {edn:?}")))?
Edn::Map(m) => m
.0
.iter()
.map(|(key, e)| {
Ok((
key.to_string(),
Deserialize::deserialize(e).map_err(|_| {
Error::Deserialize(format!(
"Cannot safely deserialize {:?} to {}",
edn, "BTreeMap"
))
})?,
Deserialize::deserialize(e).map_err(|_| Error::deserialize("BTreeMap"))?,
))
})
.collect::<Result<Self, Error>>(),
_ => Err(build_deserialize_error(edn, any::type_name::<Self>())),
_ => Err(build_deserialize_error(any::type_name::<Self>())),
}
}
}
Expand All @@ -254,19 +237,12 @@ where
{
fn deserialize(edn: &Edn) -> Result<Self, Error> {
match edn {
Edn::Set(_) => edn
.set_iter()
.ok_or_else(|| Error::Iter(format!("Could not create iter from {edn:?}")))?
.map(|e| {
Deserialize::deserialize(e).map_err(|_| {
Error::Deserialize(format!(
"Cannot safely deserialize {:?} to {}",
edn, "HashSet"
))
})
})
.collect::<Result<Self, Error>>(),
_ => Err(build_deserialize_error(edn, any::type_name::<Self>())),
Edn::Set(s) => {
s.0.iter()
.map(|e| Deserialize::deserialize(e).map_err(|_| Error::deserialize("HashSet")))
.collect::<Result<Self, Error>>()
}
_ => Err(build_deserialize_error(any::type_name::<Self>())),
}
}
}
Expand All @@ -278,19 +254,12 @@ where
{
fn deserialize(edn: &Edn) -> Result<Self, Error> {
match edn {
Edn::Set(_) => edn
.set_iter()
.ok_or_else(|| Error::Iter(format!("Could not create iter from {edn:?}")))?
.map(|e| {
Deserialize::deserialize(e).map_err(|_| {
Error::Deserialize(format!(
"Cannot safely deserialize {:?} to {}",
edn, "BTreeSet"
))
})
})
Edn::Set(s) => s
.0
.iter()
.map(|e| Deserialize::deserialize(e).map_err(|_| Error::deserialize("BTreeSet")))
.collect::<Result<Self, Error>>(),
_ => Err(build_deserialize_error(edn, any::type_name::<Self>())),
_ => Err(build_deserialize_error(any::type_name::<Self>())),
}
}
}
Expand Down Expand Up @@ -348,12 +317,7 @@ where
/// let bad_edn_str = "{:name \"rose\" :age \"some text\" }";
/// let person: Result<Person, EdnError> = edn_rs::from_str(bad_edn_str);
///
/// assert_eq!(
/// person,
/// Err(EdnError::Deserialize(
/// "couldn't convert `\"some text\"` into `uint`".to_string()
/// ))
/// );
/// println!("{:?}", person);
/// ```
pub fn from_str<T: Deserialize>(s: &str) -> Result<T, Error> {
let edn = Edn::from_str(s)?;
Expand Down Expand Up @@ -407,12 +371,7 @@ pub fn from_str<T: Deserialize>(s: &str) -> Result<T, Error> {
/// }));
/// let person: Result<Person, EdnError> = edn_rs::from_edn(&bad_edn);
///
/// assert_eq!(
/// person,
/// Err(EdnError::Deserialize(
/// "couldn't convert `\"some text\"` into `uint`".to_string()
/// ))
/// );
/// println!("{:?}", person);
/// ```
pub fn from_edn<T: Deserialize>(edn: &Edn) -> Result<T, Error> {
T::deserialize(edn)
Expand Down
Loading

0 comments on commit bc5bc8f

Please sign in to comment.