From 758a3f807210c3009cfb71f409cecc9325f437dc Mon Sep 17 00:00:00 2001 From: Paul van Tilburg Date: Sat, 1 Oct 2022 13:27:06 +0200 Subject: [PATCH] Add support for converting from/to Url structs (closes: #1) * Introduce and document the `url` feature * Implement `From<&GeoUri>` and `From` for `Url` * Implement `TryFrom<&Url>` and `TryFrom` for `GeoUri` * Add and extend tests --- Cargo.toml | 4 +++ README.md | 29 +++++++++++++++++++++ src/lib.rs | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index c6ab0bc..f2c1e13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,9 +9,13 @@ readme = "README.md" license = "MIT" keywords = ["geolocation", "uri", "parser", "rfc5870"] +[features] +url = ["dep:url"] + [dependencies] derive_builder = "0.11.2" thiserror = "1.0.35" +url = { version = "2.3.1", optional = true } [dev-dependencies] float_eq = "1.0.0" diff --git a/README.md b/README.md index 0a1d4a0..6741228 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,35 @@ let geo_uri = GeoUri::try_from((52.107, 5.134)).expect("valid coordinates"); let geo_uri = GeoUri::try_from((52.107, 5.134, 3.6)).expect("valid coordinates"); ``` +### Feature: `url` + +You can enable the `url` feature to convert from and to +[`Url`](https://docs.rs/url/2/url/struct.Url.html) structs from the +[`url`](https://docs.rs/url/2/url) crate. + +Enable the feature in your `Cargo.toml` first: + +```toml +geo-uri = { version = "X.Y.Z", features = ["url"] } +``` + +Then you can do: + +```rust +use geo_uri::GeoUri; +use url::Url; + +let url = Url::parse("geo:52.107,5.134,3.6").expect("valid URL"); +let geo_uri = GeoUri::try_from(&url).expect("valid geo URI"); +let geo_url = Url::from(geo_uri); + +assert_eq!(url, geo_url); +``` + +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! + ## License geo-uri-rs is licensed under the MIT license (see the `LICENSE` file or diff --git a/src/lib.rs b/src/lib.rs index 1d12c7f..58f7eca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,8 @@ use std::fmt; use std::num::ParseFloatError; use std::str::FromStr; +#[cfg(feature = "url")] +use url::Url; use derive_builder::Builder; use thiserror::Error; @@ -452,6 +454,20 @@ impl fmt::Display for GeoUri { } } +#[cfg(feature = "url")] +impl From<&GeoUri> for Url { + fn from(geo_uri: &GeoUri) -> Self { + Url::parse(&geo_uri.to_string()).expect("valid URL") + } +} + +#[cfg(feature = "url")] +impl From for Url { + fn from(geo_uri: GeoUri) -> Self { + Url::from(&geo_uri) + } +} + impl FromStr for GeoUri { type Err = Error; @@ -499,6 +515,24 @@ impl TryFrom<(f64, f64, f64)> for GeoUri { } } +#[cfg(feature = "url")] +impl TryFrom<&Url> for GeoUri { + type Error = Error; + + fn try_from(url: &Url) -> Result { + GeoUri::parse(url.as_str()) + } +} + +#[cfg(feature = "url")] +impl TryFrom for GeoUri { + type Error = Error; + + fn try_from(url: Url) -> Result { + GeoUri::try_from(&url) + } +} + impl PartialEq for GeoUri { fn eq(&self, other: &Self) -> bool { // In the WGS-84 CRS the the longitude is ignored for the poles. @@ -787,6 +821,25 @@ mod tests { assert_eq!(&geo_uri.to_string(), "geo:52.107,5.134,3.6;u=25000"); } + #[cfg(feature = "url")] + #[test] + fn geo_uri_from() { + let geo_uri = GeoUri { + crs: CoordRefSystem::Wgs84, + latitude: 52.107, + longitude: 5.134, + altitude: Some(3.6), + uncertainty: Some(1000.0), + }; + let url = Url::from(&geo_uri); + assert_eq!(url.scheme(), "geo"); + assert_eq!(url.path(), "52.107,5.134,3.6;u=1000"); + + let url = Url::from(geo_uri); + assert_eq!(url.scheme(), "geo"); + assert_eq!(url.path(), "52.107,5.134,3.6;u=1000"); + } + #[test] fn geo_uri_from_str() -> Result<(), Error> { let geo_uri = GeoUri::from_str("geo:52.107,5.134")?; @@ -800,12 +853,14 @@ mod tests { #[test] fn geo_uri_try_from() -> Result<(), Error> { + // &str let geo_uri = GeoUri::try_from("geo:52.107,5.134")?; assert_float_eq!(geo_uri.latitude, 52.107, abs <= 0.001); assert_float_eq!(geo_uri.longitude, 5.134, abs <= 0.001); assert_eq!(geo_uri.altitude, None); assert_eq!(geo_uri.uncertainty, None); + // (f64, f64) let geo_uri = GeoUri::try_from((51.107, 5.134))?; assert_float_eq!(geo_uri.latitude, 51.107, abs <= 0.001); assert_float_eq!(geo_uri.longitude, 5.134, abs <= 0.001); @@ -821,6 +876,7 @@ mod tests { Err(Error::OutOfRangeLongitude) ); + // (f64, f64, f64) let geo_uri = GeoUri::try_from((51.107, 5.134, 3.6))?; assert_float_eq!(geo_uri.latitude, 51.107, abs <= 0.001); assert_float_eq!(geo_uri.longitude, 5.134, abs <= 0.001); @@ -839,6 +895,26 @@ mod tests { Ok(()) } + #[cfg(feature = "url")] + #[test] + fn geo_uri_try_from_url() -> Result<(), Error> { + // Url + let url = Url::parse("geo:51.107,5.134,3.6;crs=wgs84;u=1000;foo=bar").expect("valid URL"); + let geo_uri = GeoUri::try_from(&url)?; + assert_float_eq!(geo_uri.latitude, 51.107, abs <= 0.001); + assert_float_eq!(geo_uri.longitude, 5.134, abs <= 0.001); + assert_float_eq!(geo_uri.altitude.unwrap(), 3.6, abs <= 0.1); + assert_eq!(geo_uri.uncertainty, Some(1000.0)); + + let geo_uri = GeoUri::try_from(url)?; + assert_float_eq!(geo_uri.latitude, 51.107, abs <= 0.001); + assert_float_eq!(geo_uri.longitude, 5.134, abs <= 0.001); + assert_float_eq!(geo_uri.altitude.unwrap(), 3.6, abs <= 0.1); + assert_eq!(geo_uri.uncertainty, Some(1000.0)); + + Ok(()) + } + #[test] fn geo_uri_partial_eq() -> Result<(), GeoUriBuilderError> { let geo_uri = GeoUri::builder()