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] [features]
url = ["dep:url"] url = ["dep:url"]
serde = ["dep:serde"]
[dependencies] [dependencies]
derive_builder = "0.11.2" derive_builder = "0.11.2"
serde = { version = "1.0.145", optional = true }
thiserror = "1.0.35" thiserror = "1.0.35"
url = { version = "2.3.1", optional = true } url = { version = "2.3.1", optional = true }
[dev-dependencies] [dev-dependencies]
float_eq = "1.0.0" 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 not always the other way around! This is because the format of the coordinates
and parameters after the URI scheme "geo:" may be invalid! 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 ## License
geo-uri-rs is licensed under the MIT license (see the `LICENSE` file or 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::fmt;
use std::num::ParseFloatError; use std::num::ParseFloatError;
use std::str::FromStr; use std::str::FromStr;
#[cfg(feature = "url")]
use url::Url;
use derive_builder::Builder; use derive_builder::Builder;
#[cfg(feature = "serde")]
use serde::{
de::{Deserialize, Visitor},
ser::Serialize,
};
use thiserror::Error; use thiserror::Error;
#[cfg(feature = "url")]
use url::Url;
/// The scheme name of a geo URI. /// The scheme name of a geo URI.
const URI_SCHEME_NAME: &str = "geo"; 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 { impl fmt::Display for GeoUri {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { 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 { impl TryFrom<&str> for GeoUri {
type Error = Error; type Error = Error;
@ -579,6 +625,8 @@ impl GeoUriBuilder {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use float_eq::assert_float_eq; use float_eq::assert_float_eq;
#[cfg(feature = "serde")]
use serde_test::{assert_de_tokens_error, assert_tokens, Token};
use super::*; use super::*;
@ -856,6 +904,28 @@ mod tests {
Ok(()) 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] #[test]
fn geo_uri_try_from() -> Result<(), Error> { fn geo_uri_try_from() -> Result<(), Error> {
// &str // &str