Add support for converting from/to Url structs (closes: #1)

* Introduce and document the `url` feature
* Implement `From<&GeoUri>` and `From<GeoUri>` for `Url`
* Implement `TryFrom<&Url>` and `TryFrom<Url>` for `GeoUri`
* Add and extend tests
This commit is contained in:
Paul van Tilburg 2022-10-01 13:27:06 +02:00
parent 2628e96740
commit 758a3f8072
Signed by: paul
GPG Key ID: C6DE073EDA9EEC4D
3 changed files with 109 additions and 0 deletions

View File

@ -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"

View File

@ -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

View File

@ -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<GeoUri> 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<Self, Self::Error> {
GeoUri::parse(url.as_str())
}
}
#[cfg(feature = "url")]
impl TryFrom<Url> for GeoUri {
type Error = Error;
fn try_from(url: Url) -> Result<Self, Self::Error> {
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()