Skip to content

Commit

Permalink
Makes Iri and IriRef implement Serde (De)Serialize traits
Browse files Browse the repository at this point in the history
  • Loading branch information
Tpt committed Mar 26, 2022
1 parent 9888ac5 commit c4ecf68
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 3 deletions.
8 changes: 5 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,17 @@ jobs:
- uses: actions/checkout@v1
- run: rustup update
- run: rustup component add clippy
- run: cargo clippy --all --all-targets
- run: cargo clippy --all-targets
- run: cargo clippy --all-targets --all-features

test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- run: rustup update
- run: cargo build
- run: cargo test --verbose --all --all-targets
- run: cargo build --all-features
- run: cargo test --verbose
- run: cargo test --verbose --all-features
env:
RUST_BACKTRACE: 1

Expand Down
7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,16 @@ Simple and fast implementation of IRI validation and relative IRI resolution
"""
edition = "2018"

[dependencies]
serde = { version = "1", optional = true }

[dev-dependencies]
criterion = "0.3"
serde_test = "1"

[[bench]]
name = "lib"
harness = false

[package.metadata.docs.rs]
all-features = true
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ assert_eq!(iri.query(), None);
assert_eq!(iri.fragment(), Some("foo"));
```

If [`serde`](https://serde.rs/) is available, `Iri` and `IriRef` implement the `Serialize` and `Deserialize` traits and encode the IRI as a string.


## License

Expand Down
35 changes: 35 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#![doc = include_str!("../README.md")]
#![deny(unsafe_code)]

#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::borrow::{Borrow, Cow};
use std::cmp::Ordering;
use std::convert::{TryFrom, TryInto};
Expand Down Expand Up @@ -443,6 +445,22 @@ impl<'a> From<&'a IriRef<Cow<'a, str>>> for IriRef<&'a str> {
}
}

#[cfg(feature = "serde")]
impl<T: Serialize> Serialize for IriRef<T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.iri.serialize(serializer)
}
}

#[cfg(feature = "serde")]
impl<'de, T: Deref<Target = str> + Deserialize<'de>> Deserialize<'de> for IriRef<T> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
use serde::de::Error;

Self::parse(T::deserialize(deserializer)?).map_err(D::Error::custom)
}
}

/// A [RFC 3987](https://www.ietf.org/rfc/rfc3987.html) IRI.
///
/// Instances of this type are guaranteed to be absolute,
Expand Down Expand Up @@ -861,6 +879,23 @@ impl<T: Deref<Target = str>> TryFrom<IriRef<T>> for Iri<T> {
}
}

#[cfg(feature = "serde")]
impl<T: Serialize> Serialize for Iri<T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.0.serialize(serializer)
}
}

#[cfg(feature = "serde")]
impl<'de, T: Deref<Target = str> + Deserialize<'de>> Deserialize<'de> for Iri<T> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
use serde::de::Error;
IriRef::deserialize(deserializer)?
.try_into()
.map_err(D::Error::custom)
}
}

/// An error raised during [`Iri`](struct.Iri.html) validation.
#[derive(Debug)]
pub struct IriParseError {
Expand Down
48 changes: 48 additions & 0 deletions tests/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#![allow(clippy::eq_op)]
use oxiri::{Iri, IriRef};
#[cfg(feature = "serde")]
use serde_test::{assert_de_tokens, assert_de_tokens_error, assert_tokens, Token};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

Expand Down Expand Up @@ -556,3 +558,49 @@ fn test_str() {
let iri = Iri::parse("http://example.com").unwrap();
assert!(iri.starts_with("http://"));
}

#[cfg(feature = "serde")]
#[test]
fn test_iriref_serde_impl() {
assert_tokens(
&IriRef::parse("//example.com").unwrap(),
&[Token::BorrowedStr("//example.com")],
);
assert_tokens(
&IriRef::parse("//example.com".to_string()).unwrap(),
&[Token::String("//example.com")],
);
assert_de_tokens(
&IriRef::parse("//example.com".to_string()).unwrap(),
&[Token::BorrowedStr("//example.com")],
);
assert_de_tokens_error::<IriRef<String>>(
&[Token::String(":")],
"No scheme found in an absolute IRI",
);
}

#[cfg(feature = "serde")]
#[test]
fn test_iri_serde_impl() {
assert_tokens(
&Iri::parse("http://example.com").unwrap(),
&[Token::BorrowedStr("http://example.com")],
);
assert_tokens(
&Iri::parse("http://example.com".to_string()).unwrap(),
&[Token::String("http://example.com")],
);
assert_de_tokens(
&Iri::parse("http://example.com".to_string()).unwrap(),
&[Token::BorrowedStr("http://example.com")],
);
assert_de_tokens_error::<Iri<String>>(
&[Token::String(":")],
"No scheme found in an absolute IRI",
);
assert_de_tokens_error::<Iri<String>>(
&[Token::String("//example.com")],
"No scheme found in an absolute IRI",
);
}

0 comments on commit c4ecf68

Please sign in to comment.