#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc = include_str!("../README.md")]
#![warn(
clippy::all,
missing_copy_implementations,
missing_debug_implementations,
rust_2018_idioms,
rustdoc::broken_intra_doc_links,
trivial_casts,
trivial_numeric_casts,
renamed_and_removed_lints,
unsafe_code,
unstable_features,
unused_import_braces,
unused_qualifications
)]
#![deny(missing_docs)]
use std::fmt;
use std::num::ParseFloatError;
use std::str::FromStr;
#[cfg(feature = "serde")]
use serde_core::{
de::{Deserialize, Visitor},
ser::Serialize,
};
use thiserror::Error;
#[cfg(feature = "url")]
use url::Url;
const URI_SCHEME_NAME: &str = "geo";
#[derive(Debug, Error, Eq, PartialEq)]
pub enum Error {
#[error("Invalid coordinate in geo URI: {0}")]
InvalidCoord(ParseFloatError),
#[error("Invalid coordinate reference system")]
InvalidCoordRefSystem,
#[error("Invalid distance in geo URI: {0}")]
InvalidUncertainty(ParseFloatError),
#[error("Missing coordinates in geo URI")]
MissingCoords,
#[error("Missing latitude coordinate in geo URI")]
MissingLatitude,
#[error("Missing longitude coordinate in geo URI")]
MissingLongitude,
#[error("Missing geo URI scheme")]
MissingScheme,
#[error("Latitude coordinate is out of range")]
OutOfRangeLatitude,
#[error("Longitude coordinate is out of range")]
OutOfRangeLongitude,
#[error("Uncertainty distance not positive")]
OutOfRangeUncertainty,
}
#[non_exhaustive]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum CoordRefSystem {
Wgs84,
}
impl CoordRefSystem {
pub fn validate(&self, latitude: f64, longitude: f64) -> Result<(), Error> {
if !(-90.0..=90.0).contains(&latitude) {
return Err(Error::OutOfRangeLatitude);
}
if !(-180.0..=180.0).contains(&longitude) {
return Err(Error::OutOfRangeLongitude);
}
Ok(())
}
}
impl Default for CoordRefSystem {
fn default() -> Self {
Self::Wgs84
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct GeoUri {
crs: CoordRefSystem,
latitude: f64,
longitude: f64,
altitude: Option<f64>,
uncertainty: Option<f64>,
}
impl GeoUri {
pub fn builder() -> GeoUriBuilder {
GeoUriBuilder::default()
}
pub fn parse(uri: &str) -> Result<Self, Error> {
let uri = uri.to_ascii_lowercase();
let uri_path = uri.strip_prefix("geo:").ok_or(Error::MissingScheme)?;
let mut parts = uri_path.split(';');
let coords_part = parts.next().expect("Split always yields at least one part");
let mut coords = if coords_part.is_empty() {
return Err(Error::MissingCoords);
} else {
coords_part.splitn(3, ',')
};
let latitude = coords
.next()
.ok_or(Error::MissingLatitude) .and_then(|lat_s| lat_s.parse().map_err(Error::InvalidCoord))?;
let longitude = coords
.next()
.ok_or(Error::MissingLongitude)
.and_then(|lon_s| lon_s.parse().map_err(Error::InvalidCoord))?;
let altitude = coords
.next()
.map(|alt_s| alt_s.parse().map_err(Error::InvalidCoord))
.transpose()?;
let mut param_parts = parts.flat_map(|part| part.split_once('='));
let (crs, uncertainty) = match param_parts.next() {
Some(("crs", value)) => {
if value != "wgs84" {
return Err(Error::InvalidCoordRefSystem);
}
match param_parts.next() {
Some(("u", value)) => (
CoordRefSystem::Wgs84,
Some(value.parse().map_err(Error::InvalidUncertainty)?),
),
Some(_) | None => (CoordRefSystem::Wgs84, None),
}
}
Some(("u", value)) => (
CoordRefSystem::default(),
Some(value.parse().map_err(Error::InvalidUncertainty)?),
),
Some(_) | None => (CoordRefSystem::default(), None),
};
let geo_uri = GeoUri {
crs,
latitude,
longitude,
altitude,
uncertainty,
};
geo_uri.validate()?;
Ok(geo_uri)
}
pub fn latitude(&self) -> f64 {
self.latitude
}
pub fn set_latitude(&mut self, latitude: f64) -> Result<(), Error> {
self.crs.validate(latitude, self.longitude)?;
self.latitude = latitude;
Ok(())
}
pub fn longitude(&self) -> f64 {
self.longitude
}
pub fn set_longitude(&mut self, longitude: f64) -> Result<(), Error> {
self.crs.validate(self.latitude, longitude)?;
self.longitude = longitude;
Ok(())
}
pub fn altitude(&self) -> Option<f64> {
self.altitude
}
pub fn set_altitude(&mut self, altitude: Option<f64>) {
self.altitude = altitude;
}
pub fn uncertainty(&self) -> Option<f64> {
self.uncertainty
}
pub fn set_uncertainty(&mut self, uncertainty: Option<f64>) -> Result<(), Error> {
if let Some(unc) = uncertainty {
if unc < 0.0 {
return Err(Error::OutOfRangeUncertainty);
}
}
self.uncertainty = uncertainty;
Ok(())
}
fn validate(&self) -> Result<(), Error> {
self.crs.validate(self.latitude, self.longitude)?;
if let Some(unc) = self.uncertainty {
if unc < 0.0 {
return Err(Error::OutOfRangeUncertainty);
}
}
Ok(())
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct GeoUriBuilder {
crs: Option<CoordRefSystem>,
latitude: Option<f64>,
longitude: Option<f64>,
altitude: Option<f64>,
uncertainty: Option<f64>,
}
impl GeoUriBuilder {
pub fn crs(&mut self, value: CoordRefSystem) -> &mut Self {
self.crs = Some(value);
self
}
pub fn latitude(&mut self, value: f64) -> &mut Self {
self.latitude = Some(value);
self
}
pub fn longitude(&mut self, value: f64) -> &mut Self {
self.longitude = Some(value);
self
}
pub fn altitude(&mut self, value: f64) -> &mut Self {
self.altitude = Some(value);
self
}
#[allow(unused_mut)]
pub fn uncertainty(&mut self, value: f64) -> &mut Self {
self.uncertainty = Some(value);
self
}
pub fn build(&self) -> Result<GeoUri, GeoUriBuilderError> {
self.validate()?;
Ok(GeoUri {
crs: self.crs.unwrap_or_default(),
latitude: self
.latitude
.ok_or(GeoUriBuilderError::UninitializedField("latitude"))?,
longitude: self
.longitude
.ok_or(GeoUriBuilderError::UninitializedField("longitude"))?,
altitude: self.altitude,
uncertainty: self.uncertainty,
})
}
fn validate(&self) -> Result<(), GeoUriBuilderError> {
self.crs.unwrap_or_default().validate(
self.latitude.unwrap_or_default(),
self.longitude.unwrap_or_default(),
)?;
if let Some(unc) = self.uncertainty {
if unc < 0.0 {
Err(Error::OutOfRangeUncertainty)?;
}
}
Ok(())
}
}
#[non_exhaustive]
#[derive(Debug, Error)]
pub enum GeoUriBuilderError {
#[error("uninitialized field `{0}`")]
UninitializedField(&'static str),
#[error("validation error: {0}")]
ValidationError(#[from] Error),
}
#[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_core::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_core::Deserializer<'de>,
{
deserializer.deserialize_str(GeoUriVisitor)
}
}
impl fmt::Display for GeoUri {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
latitude,
longitude,
..
} = self;
write!(f, "{URI_SCHEME_NAME}:{latitude},{longitude}")?;
if let Some(altitude) = self.altitude {
write!(f, ",{altitude}")?;
}
if let Some(uncertainty) = self.uncertainty {
write!(f, ";u={uncertainty}")?;
}
Ok(())
}
}
#[cfg(feature = "url")]
#[cfg_attr(docsrs, doc(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")]
#[cfg_attr(docsrs, doc(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;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse(s)
}
}
#[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_core::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl TryFrom<&str> for GeoUri {
type Error = Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Self::parse(value)
}
}
impl TryFrom<(f64, f64)> for GeoUri {
type Error = Error;
fn try_from((latitude, longitude): (f64, f64)) -> Result<Self, Self::Error> {
let geo_uri = GeoUri {
latitude,
longitude,
..Default::default()
};
geo_uri.validate()?;
Ok(geo_uri)
}
}
impl TryFrom<(f64, f64, f64)> for GeoUri {
type Error = Error;
fn try_from((latitude, longitude, altitude): (f64, f64, f64)) -> Result<Self, Self::Error> {
let geo_uri = GeoUri {
latitude,
longitude,
altitude: Some(altitude),
..Default::default()
};
geo_uri.validate()?;
Ok(geo_uri)
}
}
#[cfg(feature = "url")]
#[cfg_attr(docsrs, doc(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")]
#[cfg_attr(docsrs, doc(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 {
let ignore_longitude = self.crs == CoordRefSystem::Wgs84 && self.latitude.abs() == 90.0;
self.crs == other.crs
&& self.latitude == other.latitude
&& (ignore_longitude || self.longitude == other.longitude)
&& self.altitude == other.altitude
&& self.uncertainty == other.uncertainty
}
}
#[cfg(test)]
mod tests {
#[cfg(feature = "serde")]
use serde_test::{assert_de_tokens_error, assert_tokens, Token};
use super::*;
#[test]
fn coord_ref_system_default() {
assert_eq!(CoordRefSystem::default(), CoordRefSystem::Wgs84);
}
#[test]
fn coord_ref_system_validate() {
let crs = CoordRefSystem::Wgs84;
assert_eq!(crs.validate(52.107, 5.134), Ok(()));
assert_eq!(crs.validate(100.0, 5.134), Err(Error::OutOfRangeLatitude));
assert_eq!(
crs.validate(51.107, -200.0),
Err(Error::OutOfRangeLongitude)
);
}
#[test]
fn geo_uri_builder() -> Result<(), GeoUriBuilderError> {
let mut builder = GeoUri::builder();
assert!(matches!(
builder.build(),
Err(GeoUriBuilderError::UninitializedField("latitude"))
));
builder.latitude(52.107);
assert!(matches!(
builder.build(),
Err(GeoUriBuilderError::UninitializedField("longitude"))
));
builder.longitude(5.134);
let geo_uri = builder.build()?;
assert_eq!(geo_uri.latitude, 52.107);
assert_eq!(geo_uri.longitude, 5.134);
assert_eq!(geo_uri.altitude, None);
assert_eq!(geo_uri.uncertainty, None);
builder.latitude(100.0);
assert!(matches!(
builder.build(),
Err(GeoUriBuilderError::ValidationError(_))
));
builder.latitude(52.107).longitude(-200.0);
assert!(matches!(
builder.build(),
Err(GeoUriBuilderError::ValidationError(_))
));
builder.longitude(5.134).uncertainty(-200.0);
assert!(matches!(
builder.build(),
Err(GeoUriBuilderError::ValidationError(_))
));
Ok(())
}
#[test]
fn geo_uri_parse() -> Result<(), Error> {
let geo_uri = GeoUri::parse("geo:52.107,5.134")?;
assert_eq!(geo_uri.latitude, 52.107);
assert_eq!(geo_uri.longitude, 5.134);
assert_eq!(geo_uri.altitude, None);
assert_eq!(geo_uri.uncertainty, None);
let geo_uri = GeoUri::parse("52.107,5.134");
assert!(matches!(geo_uri, Err(Error::MissingScheme)));
let geo_uri = GeoUri::parse("geo:100.0,5.134");
assert!(matches!(geo_uri, Err(Error::OutOfRangeLatitude)));
let geo_uri = GeoUri::parse("geo:62.107,-200.0");
assert!(matches!(geo_uri, Err(Error::OutOfRangeLongitude)));
let geo_uri = GeoUri::parse("geo:geo:52.107,5.134");
assert!(matches!(geo_uri, Err(Error::InvalidCoord(_))));
let geo_uri = GeoUri::parse("geo:");
assert!(matches!(geo_uri, Err(Error::MissingCoords)));
let geo_uri = GeoUri::parse("geo:;u=5000");
assert!(matches!(geo_uri, Err(Error::MissingCoords)));
let geo_uri = GeoUri::parse("geo:52.107;u=1000");
assert!(matches!(geo_uri, Err(Error::MissingLongitude)));
let geo_uri = GeoUri::parse("geo:52.107,;u=1000");
assert!(matches!(geo_uri, Err(Error::InvalidCoord(_))));
let geo_uri = GeoUri::parse("geo:52.107,,6.50;u=1000");
assert!(matches!(geo_uri, Err(Error::InvalidCoord(_))));
let geo_uri = GeoUri::parse("geo:52.107,5.134,;u=1000");
assert!(matches!(geo_uri, Err(Error::InvalidCoord(_))));
let geo_uri = GeoUri::parse("geo:52.107,5.134,3.6")?;
assert_eq!(geo_uri.latitude, 52.107);
assert_eq!(geo_uri.longitude, 5.134);
assert_eq!(geo_uri.altitude.unwrap(), 3.6);
assert_eq!(geo_uri.uncertainty, None);
let geo_uri = GeoUri::parse("geo:52.107,5.34,3.6;u=");
assert!(matches!(geo_uri, Err(Error::InvalidUncertainty(_))));
let geo_uri = GeoUri::parse("geo:52.107,5.34,3.6;u=foo");
assert!(matches!(geo_uri, Err(Error::InvalidUncertainty(_))));
let geo_uri = GeoUri::parse("geo:52.107,5.34,3.6;crs=wgs84;u=foo");
assert!(matches!(geo_uri, Err(Error::InvalidUncertainty(_))));
let geo_uri = GeoUri::parse("geo:52.107,5.34,3.6;u=-10.0");
assert!(matches!(geo_uri, Err(Error::OutOfRangeUncertainty)));
let geo_uri = GeoUri::parse("geo:52.107,5.134,3.6;u=25000")?;
assert_eq!(geo_uri.latitude, 52.107);
assert_eq!(geo_uri.longitude, 5.134);
assert_eq!(geo_uri.altitude.unwrap(), 3.6);
assert_eq!(geo_uri.uncertainty, Some(25_000.0));
let geo_uri = GeoUri::parse("geo:52.107,5.134,3.6;crs=wgs84;u=25000")?;
assert_eq!(geo_uri.latitude, 52.107);
assert_eq!(geo_uri.longitude, 5.134);
assert_eq!(geo_uri.altitude.unwrap(), 3.6);
assert_eq!(geo_uri.uncertainty, Some(25_000.0));
let geo_uri = GeoUri::parse("geo:52.107,5.134,3.6;CRS=wgs84;U=25000")?;
assert_eq!(geo_uri.uncertainty, Some(25_000.0));
let geo_uri = GeoUri::parse("geo:52.107,5.134,3.6;crs=wgs84;u=25000;foo=bar")?;
assert_eq!(geo_uri.latitude, 52.107);
assert_eq!(geo_uri.longitude, 5.134);
assert_eq!(geo_uri.altitude.unwrap(), 3.6);
assert_eq!(geo_uri.uncertainty, Some(25_000.0));
let geo_uri = GeoUri::parse("geo:52.107,5.34,3.6;crs=foo");
assert!(matches!(geo_uri, Err(Error::InvalidCoordRefSystem)));
let geo_uri = GeoUri::parse("geo:52.107,5.34,3.6;crs=wgs84")?;
assert!(matches!(geo_uri.crs, CoordRefSystem::Wgs84));
let geo_uri = GeoUri::parse("geo:13.4125,103.8667")?;
assert_eq!(geo_uri.latitude, 13.4125);
assert_eq!(geo_uri.longitude, 103.8667);
assert_eq!(geo_uri.altitude, None);
assert_eq!(geo_uri.uncertainty, None);
let geo_uri = GeoUri::parse("geo:48.2010,16.3695,183")?;
assert_eq!(geo_uri.latitude, 48.2010);
assert_eq!(geo_uri.longitude, 16.3695);
assert_eq!(geo_uri.altitude.unwrap(), 183.0);
assert_eq!(geo_uri.uncertainty, None);
let geo_uri = GeoUri::parse("geo:48.198634,16.371648;crs=wgs84;u=40")?;
assert_eq!(geo_uri.crs, CoordRefSystem::Wgs84);
assert_eq!(geo_uri.latitude, 48.198634);
assert_eq!(geo_uri.longitude, 16.371648);
assert_eq!(geo_uri.altitude, None);
assert_eq!(geo_uri.uncertainty, Some(40.0));
let geo_uri = GeoUri::parse("geo:94,0");
assert_eq!(geo_uri, Err(Error::OutOfRangeLatitude));
Ok(())
}
#[test]
fn geo_uri_validate() {
let mut geo_uri = GeoUri {
crs: CoordRefSystem::Wgs84,
latitude: 52.107,
longitude: 5.134,
altitude: None,
uncertainty: None,
};
assert_eq!(geo_uri.validate(), Ok(()));
geo_uri.latitude = 100.0;
assert_eq!(geo_uri.validate(), Err(Error::OutOfRangeLatitude));
geo_uri.latitude = 52.107;
geo_uri.longitude = -200.0;
assert_eq!(geo_uri.validate(), Err(Error::OutOfRangeLongitude));
geo_uri.longitude = 5.134;
geo_uri.uncertainty = Some(-2000.0);
assert_eq!(geo_uri.validate(), Err(Error::OutOfRangeUncertainty));
}
#[test]
fn geo_uri_get_set() {
let mut geo_uri = GeoUri {
crs: CoordRefSystem::Wgs84,
latitude: 52.107,
longitude: 5.134,
altitude: None,
uncertainty: None,
};
assert_eq!(geo_uri.latitude(), 52.107);
assert_eq!(geo_uri.longitude(), 5.134);
assert_eq!(geo_uri.altitude(), None);
assert_eq!(geo_uri.uncertainty(), None);
assert_eq!(geo_uri.set_latitude(53.107), Ok(()));
assert_eq!(geo_uri.set_latitude(100.0), Err(Error::OutOfRangeLatitude));
assert_eq!(geo_uri.latitude(), 53.107);
assert_eq!(geo_uri.set_longitude(6.134), Ok(()));
assert_eq!(
geo_uri.set_longitude(-200.0),
Err(Error::OutOfRangeLongitude)
);
assert_eq!(geo_uri.longitude(), 6.134);
geo_uri.set_altitude(Some(3.6));
assert_eq!(geo_uri.altitude(), Some(3.6));
assert_eq!(geo_uri.set_uncertainty(Some(25_000.0)), Ok(()));
assert_eq!(
geo_uri.set_uncertainty(Some(-100.0)),
Err(Error::OutOfRangeUncertainty)
);
assert_eq!(geo_uri.uncertainty(), Some(25_000.0));
}
#[test]
fn geo_uri_display() {
let mut geo_uri = GeoUri {
crs: CoordRefSystem::Wgs84,
latitude: 52.107,
longitude: 5.134,
altitude: None,
uncertainty: None,
};
assert_eq!(&geo_uri.to_string(), "geo:52.107,5.134");
geo_uri.altitude = Some(3.6);
assert_eq!(&geo_uri.to_string(), "geo:52.107,5.134,3.6");
geo_uri.uncertainty = Some(25_000.0);
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")?;
assert_eq!(geo_uri.latitude, 52.107);
assert_eq!(geo_uri.longitude, 5.134);
assert_eq!(geo_uri.altitude, None);
assert_eq!(geo_uri.uncertainty, None);
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> {
let geo_uri = GeoUri::try_from("geo:52.107,5.134")?;
assert_eq!(geo_uri.latitude, 52.107);
assert_eq!(geo_uri.longitude, 5.134);
assert_eq!(geo_uri.altitude, None);
assert_eq!(geo_uri.uncertainty, None);
let geo_uri = GeoUri::try_from((51.107, 5.134))?;
assert_eq!(geo_uri.latitude, 51.107);
assert_eq!(geo_uri.longitude, 5.134);
assert_eq!(geo_uri.altitude, None);
assert_eq!(geo_uri.uncertainty, None);
assert_eq!(
GeoUri::try_from((100.0, 5.134)),
Err(Error::OutOfRangeLatitude)
);
assert_eq!(
GeoUri::try_from((51.107, -200.0)),
Err(Error::OutOfRangeLongitude)
);
let geo_uri = GeoUri::try_from((51.107, 5.134, 3.6))?;
assert_eq!(geo_uri.latitude, 51.107);
assert_eq!(geo_uri.longitude, 5.134);
assert_eq!(geo_uri.altitude.unwrap(), 3.6);
assert_eq!(geo_uri.uncertainty, None);
assert_eq!(
GeoUri::try_from((100.0, 5.134, 3.6)),
Err(Error::OutOfRangeLatitude)
);
assert_eq!(
GeoUri::try_from((51.107, -200.0, 3.6)),
Err(Error::OutOfRangeLongitude)
);
Ok(())
}
#[cfg(feature = "url")]
#[test]
fn geo_uri_try_from_url() -> Result<(), Error> {
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_eq!(geo_uri.latitude, 51.107);
assert_eq!(geo_uri.longitude, 5.134);
assert_eq!(geo_uri.altitude.unwrap(), 3.6);
assert_eq!(geo_uri.uncertainty, Some(1000.0));
let geo_uri = GeoUri::try_from(url)?;
assert_eq!(geo_uri.latitude, 51.107);
assert_eq!(geo_uri.longitude, 5.134);
assert_eq!(geo_uri.altitude.unwrap(), 3.6);
assert_eq!(geo_uri.uncertainty, Some(1000.0));
Ok(())
}
#[test]
fn geo_uri_partial_eq() -> Result<(), GeoUriBuilderError> {
let geo_uri = GeoUri::builder()
.latitude(52.107)
.longitude(5.134)
.build()?;
let geo_uri2 = GeoUri::builder()
.latitude(52.107)
.longitude(5.134)
.build()?;
assert_eq!(geo_uri, geo_uri2);
assert_eq!(geo_uri, geo_uri.clone());
let geo_uri = GeoUri::builder().latitude(90.0).longitude(5.134).build()?;
let geo_uri2 = GeoUri::builder().latitude(90.0).longitude(5.134).build()?;
assert_eq!(geo_uri, geo_uri2);
let geo_uri = GeoUri::builder().latitude(-90.0).longitude(5.134).build()?;
let geo_uri2 = GeoUri::builder().latitude(-90.0).longitude(5.134).build()?;
assert_eq!(geo_uri, geo_uri2);
let geo_uri = GeoUri::parse("geo:90,-22.43;crs=WGS84").expect("parsable geo URI");
let geo_uri2 = GeoUri::parse("geo:90,46").expect("parsable geo URI");
assert_eq!(geo_uri, geo_uri2);
let geo_uri = GeoUri::parse("geo:22.300,-118.44").expect("parsable geo URI");
let geo_uri2 = GeoUri::parse("geo:22.3,-118.4400").expect("parsable geo URI");
assert_eq!(geo_uri, geo_uri2);
let geo_uri = GeoUri::parse("geo:66,30;u=6.500;FOo=this%2dthat").expect("parsable geo URI");
let geo_uri2 = GeoUri::parse("geo:66.0,30;u=6.5;foo=this-that").expect("parsable geo URI");
assert_eq!(geo_uri, geo_uri2);
let _geo_uri = GeoUri::parse("geo:70,20;foo=1.00;bar=white").expect("parsable geo URI");
let _geo_uri2 = GeoUri::parse("geo:70,20;foo=1;bar=white").expect("parsable geo URI");
let geo_uri = GeoUri::parse("geo:47,11;foo=blue;bar=white").expect("parsable geo URI");
let geo_uri2 = GeoUri::parse("geo:47,11;bar=white;foo=blue").expect("parsable geo URI");
assert_eq!(geo_uri, geo_uri2);
let _geo_uri = GeoUri::parse("geo:22,0;bar=Blue").expect("parsable geo URI");
let _geo_uri2 = GeoUri::parse("geo:22,0;BAR=blue").expect("parsable geo URI");
Ok(())
}
}