Skip to content

Commit

Permalink
Merge pull request #9 from kinnison/mutants
Browse files Browse the repository at this point in the history
Mutants
  • Loading branch information
kinnison committed Apr 13, 2024
2 parents cf7c8bd + 221a0fa commit fcdb525
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 86 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/target
tarpaulin-report.html
mutants.out*
129 changes: 44 additions & 85 deletions marked-yaml/src/spanned_serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ where
}
}

impl PartialEq<str> for Spanned<String> {
fn eq(&self, other: &str) -> bool {
self.inner == other
impl PartialEq<&str> for Spanned<String> {
fn eq(&self, other: &&str) -> bool {
self.inner == *other
}
}

Expand Down Expand Up @@ -140,8 +140,8 @@ where
{
type Value = Spanned<T>;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a MarkedNode of some kind")
fn expecting(&self, _formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
unreachable!()
}

fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
Expand Down Expand Up @@ -222,6 +222,36 @@ where
}
}

#[cfg(test)]
mod spanned_tests {
use super::Spanned;
use serde::{forward_to_deserialize_any, Deserialize, Deserializer};

#[test]
#[should_panic]
fn spanned_always_map() {
struct NotSpanned;
impl<'de> Deserializer<'de> for NotSpanned {
type Error = super::Error;

fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
visitor.visit_bool(false)
}

forward_to_deserialize_any! [
bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes byte_buf
unit unit_struct newtype_struct seq tuple tuple_struct
map identifier ignored_any option struct enum
];
}
type T = Spanned<bool>;
let _ = T::deserialize(NotSpanned);
}
}

// -------------------------------------------------------------------------------

/// Errors which can come from deserialisation
Expand Down Expand Up @@ -559,9 +589,6 @@ where
T: Deserialize<'de>,
{
#[cfg(not(feature = "serde-path"))]
fn inner_from_node<'de, T>(node: &'de Node) -> Result<T, FromNodeError>
where
T: Deserialize<'de>,
{
T::deserialize(NodeDeserializer::new(node)).map_err(|e| FromNodeError {
error: e,
Expand All @@ -570,9 +597,6 @@ where
}

#[cfg(feature = "serde-path")]
fn inner_from_node<'de, T>(node: &'de Node) -> Result<T, FromNodeError>
where
T: Deserialize<'de>,
{
use serde_path_to_error::Segment;

Expand Down Expand Up @@ -634,25 +658,20 @@ where
}
}
e.set_span(best_span);
FromNodeError {
error: e,
path: Some(path),
}
FromNodeError { error: e, path }
} else {
let path = render_path(e.path());
FromNodeError {
error: e.into_inner(),
path: Some(path),
path,
}
}
})
}

inner_from_node(node)
}

#[cfg(feature = "serde-path")]
fn render_path(path: &serde_path_to_error::Path) -> String {
fn render_path(path: &serde_path_to_error::Path) -> Option<String> {
use serde_path_to_error::Segment::*;
use std::fmt::Write;
let mut ret = String::new();
Expand All @@ -670,7 +689,11 @@ fn render_path(path: &serde_path_to_error::Path) -> String {
}
separator = ".";
}
ret
if ret.is_empty() {
None
} else {
Some(ret)
}
}

// -------------------------------------------------------------------------------
Expand Down Expand Up @@ -909,70 +932,6 @@ where

// -------------------------------------------------------------------------------

struct UnitVariantAccess;

impl<'de> VariantAccess<'de> for UnitVariantAccess {
type Error = Error;

fn unit_variant(self) -> Result<(), Self::Error> {
Ok(())
}

fn newtype_variant_seed<T>(self, _seed: T) -> Result<T::Value, Self::Error>
where
T: serde::de::DeserializeSeed<'de>,
{
Err(serde::de::Error::invalid_type(
Unexpected::UnitVariant,
&"newtype variant",
))
}

fn tuple_variant<V>(self, _len: usize, _visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
Err(serde::de::Error::invalid_type(
Unexpected::UnitVariant,
&"tuple variant",
))
}

fn struct_variant<V>(
self,
_fields: &'static [&'static str],
_visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
Err(serde::de::Error::invalid_type(
Unexpected::UnitVariant,
&"struct variant",
))
}
}

struct MarkedScalarNodeEnumAccess<'de> {
node: &'de MarkedScalarNode,
}

impl<'de> EnumAccess<'de> for MarkedScalarNodeEnumAccess<'de> {
type Error = Error;

type Variant = UnitVariantAccess;

fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
where
V: serde::de::DeserializeSeed<'de>,
{
seed.deserialize(self.node.into_deserializer())
.map(|v| (v, UnitVariantAccess))
}
}

// -------------------------------------------------------------------------------

impl<'de> IntoDeserializer<'de, Error> for &'de MarkedScalarNode {
type Deserializer = MarkedScalarNodeDeserializer<'de>;
fn into_deserializer(self) -> MarkedScalarNodeDeserializer<'de> {
Expand Down Expand Up @@ -1071,7 +1030,7 @@ impl<'de> Deserializer<'de> for MarkedScalarNodeDeserializer<'de> {
where
V: Visitor<'de>,
{
visitor.visit_enum(MarkedScalarNodeEnumAccess { node: self.node })
visitor.visit_enum(self.node.as_str().into_deserializer())
}

forward_to_deserialize_any! [
Expand Down
47 changes: 46 additions & 1 deletion marked-yaml/tests/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@

use std::collections::HashMap;

use marked_yaml::{from_node, from_yaml, parse_yaml, Spanned};
use marked_yaml::{from_node, from_yaml, parse_yaml, FromYamlError, Span, Spanned};
use serde::Deserialize;

const TEST_DOC: &str = r#"# Line one is a comment
top:
- level
- is always
- two
- strings
u8s: [ 0, 1, 2, 255 ]
i8s: [ -128, 0, 127 ]
Expand All @@ -24,6 +25,9 @@ kvs:
first: one
second: two
third: banana
falsy: false
truthy: true
yes: true
"#;

#[derive(Debug, Deserialize)]
Expand All @@ -39,6 +43,9 @@ struct FullTest {
looksee: EnumCheck,
known: EnumCheck,
kvs: HashMap<Spanned<String>, Spanned<String>>,
falsy: Spanned<bool>,
truthy: Spanned<bool>,
yes: Spanned<bool>,
}

#[derive(Deserialize, Debug)]
Expand Down Expand Up @@ -67,10 +74,48 @@ fn read_everything() {
let nodes = parse_yaml(0, TEST_DOC).unwrap();
let doc: FullTest = from_node(&nodes).unwrap();
println!("{doc:?}");
assert_eq!(doc.top[0].as_str(), "level");
assert!(doc.falsy == false);
assert!(doc.truthy == true);
assert_ne!(doc.truthy, doc.falsy);
assert_eq!(doc.truthy, doc.yes);
assert_eq!(doc.top[2], "two");
assert_ne!(doc.top[3], "two");
let s = String::from("s");
assert!(doc.top[0] != s);
}

#[test]
fn ergonomics() {
let doc: FullTest = from_yaml(0, TEST_DOC).unwrap();
assert_eq!(doc.kvs.get("first").map(|s| s.as_str()), Some("one"));
let k1 = Spanned::new(Span::new_blank(), "k1");
let mut map = HashMap::new();
map.insert(k1, "v1");
let k2 = Spanned::new(Span::new_blank(), "k2");
assert!(!map.contains_key(&k2));
assert!(map.contains_key("k1"));
}

#[test]
fn parse_fails() {
let err = from_yaml::<FullTest>(0, "hello world").err().unwrap();
assert!(matches!(err, FromYamlError::ParseYaml(_)));
let err = from_yaml::<FullTest>(0, "hello: world").err().unwrap();
assert!(matches!(err, FromYamlError::FromNode(_)));
let s = format!("{err}");
assert!(s.starts_with("missing field"));
#[derive(Deserialize)]
#[allow(dead_code)]
struct MiniDoc {
colour: Colour,
}
let err = from_yaml::<MiniDoc>(0, "colour: {Red: optional}")
.err()
.unwrap();
let s = format! {"{err}"};
#[cfg(feature = "serde-path")]
assert_eq!(s, "colour.Red: invalid type: map, expected String");
#[cfg(not(feature = "serde-path"))]
assert_eq!(s, "invalid type: map, expected String");
}

0 comments on commit fcdb525

Please sign in to comment.