Add support for serde (closes: #2)

* Introduce and document the `serde` feature
* Implement `serde::Deserialize` and `serde::Serialize` for `GeoUri`
* Add tests
This commit is contained in:
Paul van Tilburg 2022-10-01 15:21:56 +02:00
parent f2cb2788ab
commit 56a888d5a3
Signed by: paul
GPG Key ID: C6DE073EDA9EEC4D
3 changed files with 86 additions and 2 deletions

View File

@ -15,11 +15,14 @@ rustdoc-args = ["--cfg", "docsrs"]
[features]
url = ["dep:url"]
serde = ["dep:serde"]
[dependencies]
derive_builder = "0.11.2"
serde = { version = "1.0.145", optional = true }
thiserror = "1.0.35"
url = { version = "2.3.1", optional = true }
[dev-dependencies]
float_eq = "1.0.0"
serde_test = "1.0.145"

View File

@ -119,6 +119,17 @@ Note that it is always possible to transform a [`GeoUri`] into an [`Url`], but
not always the other way around! This is because the format of the coordinates
and parameters after the URI scheme "geo:" may be invalid!
### Feature: `serde`
If you enable the `serde` feature, [`GeoUri`] will implement
[`serde::Serialize`](https://docs.rs/serde/1/serde/trait.Serialize.html) and
[`serde::Deserialize`](https://docs.rs/serde/1/serde/trait.Deserialize.html).
See the [serde](https://serde.rs) documentation for more information.
```toml
geo-uri = { version = "X.Y.Z", features = ["serde"] }
```
## License
geo-uri-rs is licensed under the MIT license (see the `LICENSE` file or

View File

@ -19,11 +19,16 @@
use std::fmt;
use std::num::ParseFloatError;
use std::str::FromStr;
#[cfg(feature = "url")]
use url::Url;
use derive_builder::Builder;
#[cfg(feature = "serde")]
use serde::{
de::{Deserialize, Visitor},
ser::Serialize,
};
use thiserror::Error;
#[cfg(feature = "url")]
use url::Url;
/// The scheme name of a geo URI.
const URI_SCHEME_NAME: &str = "geo";
@ -433,6 +438,36 @@ impl GeoUri {
}
}
#[cfg(feature = "serde")]
struct GeoUriVisitor;
#[cfg(feature = "serde")]
impl<'de> Visitor<'de> for GeoUriVisitor {
type Value = GeoUri;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "a string starting with {URI_SCHEME_NAME}:")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
GeoUri::parse(v).map_err(E::custom)
}
}
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<'de> Deserialize<'de> for GeoUri {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_str(GeoUriVisitor)
}
}
impl fmt::Display for GeoUri {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
@ -479,6 +514,17 @@ impl FromStr for GeoUri {
}
}
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl Serialize for GeoUri {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl TryFrom<&str> for GeoUri {
type Error = Error;
@ -579,6 +625,8 @@ impl GeoUriBuilder {
#[cfg(test)]
mod tests {
use float_eq::assert_float_eq;
#[cfg(feature = "serde")]
use serde_test::{assert_de_tokens_error, assert_tokens, Token};
use super::*;
@ -856,6 +904,28 @@ mod tests {
Ok(())
}
#[cfg(feature = "serde")]
#[test]
fn geo_uri_serde() {
let geo_uri = GeoUri {
crs: CoordRefSystem::Wgs84,
latitude: 52.107,
longitude: 5.134,
altitude: Some(3.6),
uncertainty: Some(1000.0),
};
assert_tokens(&geo_uri, &[Token::String("geo:52.107,5.134,3.6;u=1000")]);
assert_de_tokens_error::<GeoUri>(
&[Token::I32(0)],
"invalid type: integer `0`, expected a string starting with geo:",
);
assert_de_tokens_error::<GeoUri>(
&[Token::String("geo:100.0,5.134,3.6")],
&format!("{}", Error::OutOfRangeLatitude),
);
}
#[test]
fn geo_uri_try_from() -> Result<(), Error> {
// &str