Start work on generating urls

This commit is contained in:
Austen Adler 2023-03-26 21:27:19 -04:00
parent 432f7259ac
commit 773063daf0
6 changed files with 176 additions and 1 deletions

61
Cargo.lock generated
View File

@ -350,6 +350,15 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
dependencies = [
"percent-encoding",
]
[[package]] [[package]]
name = "futures" name = "futures"
version = "0.3.27" version = "0.3.27"
@ -577,6 +586,16 @@ dependencies = [
"want", "want",
] ]
[[package]]
name = "idna"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.9.2" version = "1.9.2"
@ -1354,6 +1373,7 @@ dependencies = [
"nom", "nom",
"pluscodes", "pluscodes",
"thiserror", "thiserror",
"url",
"utm", "utm",
] ]
@ -1512,6 +1532,21 @@ dependencies = [
"time-core", "time-core",
] ]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.26.0" version = "1.26.0"
@ -1702,12 +1737,27 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "unicode-bidi"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.8" version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
name = "unicode-normalization"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
[[package]] [[package]]
name = "unicode-segmentation" name = "unicode-segmentation"
version = "1.10.1" version = "1.10.1"
@ -1726,6 +1776,17 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "url"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
]
[[package]] [[package]]
name = "utm" name = "utm"
version = "0.1.6" version = "0.1.6"

View File

@ -22,3 +22,4 @@ nom = "7.1.3"
pluscodes = "0.5.0" pluscodes = "0.5.0"
utm = "0.1.6" utm = "0.1.6"
thiserror = "1.0.38" thiserror = "1.0.38"
url = "2.3.1"

View File

@ -21,6 +21,13 @@ pub enum Error {
NomErr(String), NomErr(String),
#[error("No UTM zone letter")] #[error("No UTM zone letter")]
NoUtmZoneLetter, NoUtmZoneLetter,
/// The url could not be parsed at all
#[error("Could not parse URL: {0}")]
UrlParseError(url::ParseError),
/// A latitude/longitude could not be extracted from the url
#[error("Could not find lat/lon in URL: {0}")]
NoUrlLatLon(String),
} }
impl From<pluscodes::Error> for Error { impl From<pluscodes::Error> for Error {
@ -35,6 +42,12 @@ impl From<utm::WSG84ToLatLonError> for Error {
} }
} }
impl From<url::ParseError> for Error {
fn from(e: url::ParseError) -> Self {
Self::UrlParseError(e)
}
}
impl<E: std::fmt::Debug> From<nom::Err<E>> for Error { impl<E: std::fmt::Debug> From<nom::Err<E>> for Error {
fn from(e: nom::Err<E>) -> Self { fn from(e: nom::Err<E>) -> Self {
Self::NomErr(format!("{e:?}")) Self::NomErr(format!("{e:?}"))

View File

@ -1,6 +1,16 @@
use std::fmt; use std::fmt;
use crate::Error; use nom::{
character::complete::space0,
combinator::{eof, map_res},
sequence::tuple,
IResult,
};
use crate::{
common::{optional_separator, parse_f64},
Error,
};
#[derive(PartialEq, Debug, Clone, Copy)] #[derive(PartialEq, Debug, Clone, Copy)]
pub struct LatLon { pub struct LatLon {
@ -26,6 +36,23 @@ impl LatLon {
pub fn get_lon(&self) -> f64 { pub fn get_lon(&self) -> f64 {
self.lon self.lon
} }
/// Parse a latitude and longitude as two floating point numbers separated by a comma and/or whitespace only (no bearings)
/// Parse only the entire string
pub fn parse_full(i: &str) -> IResult<&str, Self> {
map_res(
tuple((
space0,
parse_f64,
optional_separator(','),
space0,
parse_f64,
space0,
eof,
)),
|(_, lat, _, _, lon, _, _)| Self::new(lat, lon),
)(i)
}
} }
impl fmt::Display for LatLon { impl fmt::Display for LatLon {

View File

@ -6,6 +6,7 @@ pub mod dmm;
pub mod dms; pub mod dms;
pub mod latlon; pub mod latlon;
pub mod plus; pub mod plus;
pub mod urls;
pub mod utm; pub mod utm;
pub use error::Error; pub use error::Error;
// pub mod xpin; // pub mod xpin;

View File

@ -0,0 +1,72 @@
use crate::{Error, LatLon};
use std::str::FromStr;
use url::Url;
#[derive(PartialEq, Debug, Clone)]
pub struct CoordinateUrls {
google_maps: String,
latlon: LatLon,
}
impl CoordinateUrls {
pub fn parse(i: &str) -> Result<Self, Error> {
let url = Url::parse(i.trim())?;
// Try to parse a url from google maps
url.query_pairs()
.find_map(|(key, value)| {
if key != "query" {
return None;
}
LatLon::parse_full(value.as_ref())
.map(|(_, ret)| ret)
.ok()
.map(Self::try_from)
})
.ok_or_else(|| Error::NoUrlLatLon(i.to_string()))?
}
}
impl From<&CoordinateUrls> for LatLon {
fn from(coordinate: &CoordinateUrls) -> LatLon {
coordinate.latlon
}
}
impl TryFrom<LatLon> for CoordinateUrls {
type Error = Error;
fn try_from(latlon: LatLon) -> Result<Self, Self::Error> {
let latlon_str = format!("{},{}", latlon.get_lat(), latlon.get_lon());
Ok(Self {
google_maps: String::from(Url::parse_with_params(
"https://www.google.com/maps/search/",
&[("api", "1"), ("query", &latlon_str)],
)?),
latlon,
})
}
}
impl FromStr for CoordinateUrls {
type Err = Error;
fn from_str(i: &str) -> Result<Self, Self::Err> {
Self::parse(i).map_err(Into::into)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_general() {
assert!(dbg!(CoordinateUrls::parse(
"https://www.google.com/maps/search/?query=27,23"
))
.is_ok());
}
}