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:
parent
f2cb2788ab
commit
56a888d5a3
|
@ -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"
|
||||||
|
|
11
README.md
11
README.md
|
@ -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
|
||||||
|
|
74
src/lib.rs
74
src/lib.rs
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue