use crate::bbox::BBox;
use crate::errors::UtilesCoreResult;
use crate::UtilesCoreError;
use serde_json::Value;
pub fn parse_bbox_json(string: &str) -> UtilesCoreResult<BBox> {
let s = string.trim();
if s.starts_with('{') {
let v: Value = serde_json::from_str(s)?;
if v["bbox"].is_array() {
let bbox: (f64, f64, f64, f64) = serde_json::from_value(v["bbox"].clone())?;
return Ok(BBox::from(bbox));
}
return Err(UtilesCoreError::InvalidBbox(
"Invalid bbox: ".to_string() + s,
));
}
let v: Value = serde_json::from_str(s)?;
let bbox = match v.as_array().map(Vec::len) {
Some(0 | 1 | 3) => Err(UtilesCoreError::InvalidBbox(
"Invalid bbox: ".to_string() + s,
)),
Some(2) => {
let coord: (f64, f64) = serde_json::from_value::<(f64, f64)>(v)?;
Ok(BBox::new(coord.0, coord.1, coord.0, coord.1))
}
Some(4) => {
let bbox: (f64, f64, f64, f64) = serde_json::from_value(v)?;
Ok(BBox::from(bbox))
}
_ => {
let bbox_vec_as_arr = v.as_array();
match bbox_vec_as_arr {
None => Err(UtilesCoreError::InvalidBbox(
"Invalid bbox: ".to_string() + s,
)),
Some(bbox_vec) => {
let bbox_vec =
bbox_vec.iter().take(4).cloned().collect::<Vec<Value>>();
Ok(BBox::from(serde_json::from_value::<(f64, f64, f64, f64)>(
Value::Array(bbox_vec),
)?))
}
}
}
};
bbox
}
pub fn parse_bbox(string: &str) -> UtilesCoreResult<BBox> {
let s = string.trim();
if s.starts_with('{') || s.starts_with('[') {
let bbox = parse_bbox_json(s);
return bbox;
}
let parts: Vec<f64> = if s.contains(',') {
s.split(',')
.map(str::trim)
.filter_map(|p| p.parse::<f64>().ok())
.collect()
} else if s.contains(' ') {
s.split(' ')
.map(str::trim)
.filter_map(|p| p.parse::<f64>().ok())
.collect()
} else {
vec![]
};
if parts.len() == 4 {
if parts[3] < parts[1] {
Err(UtilesCoreError::InvalidBbox(
"Invalid bbox: ".to_string() + s + " (north < south)",
))
} else {
Ok(BBox::new(parts[0], parts[1], parts[2], parts[3]))
}
} else {
Err(UtilesCoreError::InvalidBbox(
"Invalid bbox: ".to_string() + s,
))
}
}
pub fn parse_bbox_ext(string: &str) -> UtilesCoreResult<BBox> {
let str_lower = string
.trim()
.trim_matches(|c| c == '\'' || c == '"')
.to_lowercase();
let r = match str_lower.as_str() {
"world" | "planet" | "all" | "*" => Ok(BBox::new(-180.0, -90.0, 180.0, 90.0)),
"n" | "north" => Ok(BBox::new(-180.0, 0.0, 180.0, 90.0)),
"s" | "south" => Ok(BBox::new(-180.0, -90.0, 180.0, 0.0)),
"e" | "east" => Ok(BBox::new(0.0, -90.0, 180.0, 90.0)),
"w" | "west" => Ok(BBox::new(-180.0, -90.0, 0.0, 90.0)),
"ne" | "northeast" => Ok(BBox::new(0.0, 0.0, 180.0, 90.0)),
"nw" | "northwest" => Ok(BBox::new(-180.0, 0.0, 0.0, 90.0)),
"se" | "southeast" => Ok(BBox::new(0.0, -90.0, 180.0, 0.0)),
"sw" | "southwest" => Ok(BBox::new(-180.0, -90.0, 0.0, 0.0)),
_ => parse_bbox(&str_lower),
};
r
}
#[must_use]
pub fn parse_uint_strings(input: &str) -> Vec<&str> {
let mut blocks = Vec::new();
let mut start = None;
for (i, c) in input.char_indices() {
if c.is_ascii_digit() {
if start.is_none() {
start = Some(i);
}
} else if let Some(s) = start {
blocks.push(&input[s..i]);
start = None;
}
}
if let Some(s) = start {
blocks.push(&input[s..]);
}
blocks
}
#[must_use]
pub fn parse_uints(input: &str) -> Vec<u64> {
parse_uint_strings(input)
.iter()
.flat_map(|s| s.parse::<u64>())
.collect()
}
#[must_use]
pub fn parse_int_strings(input: &str) -> Vec<&str> {
let mut blocks = Vec::new();
let mut start = None;
let mut is_negative = false;
for (i, c) in input.char_indices() {
match c {
'-' => {
if start.is_none() {
start = Some(i);
is_negative = true; } else if let Some(s) = start {
if !is_negative || s < i - 1 {
blocks.push(&input[s..i]);
}
start = Some(i);
is_negative = true;
}
}
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => {
if start.is_none() || is_negative {
start = Some(i - usize::from(is_negative)); }
is_negative = false; }
_ => {
if let Some(s) = start {
if !is_negative || s < i - 1 {
blocks.push(&input[s..i]);
}
}
start = None;
is_negative = false;
}
}
}
if let Some(s) = start {
if !is_negative || s < input.len() - 1 {
blocks.push(&input[s..]);
}
}
blocks
}
#[must_use]
pub fn parse_ints(input: &str) -> Vec<i64> {
parse_int_strings(input)
.iter()
.flat_map(|s| s.parse::<i64>())
.collect()
}
#[must_use]
pub fn parse_float_blocks(input: &str) -> Vec<&str> {
let mut blocks = Vec::new();
let mut start = None; let mut has_decimal = false; let mut has_digit = false;
for (i, c) in input.char_indices() {
match c {
'0'..='9' => {
if start.is_none() {
start = Some(i);
}
has_digit = true;
}
'-' => {
if start.is_none() && !has_digit {
start = Some(i);
} else if has_digit || has_decimal || start.is_some() {
if let Some(s) = start {
if has_digit {
blocks.push(&input[s..i]);
}
}
start = Some(i); has_decimal = false;
has_digit = false;
}
}
'.' => {
if !has_decimal && start.is_none() {
start = Some(i);
has_decimal = true;
} else if has_decimal || start.is_none() {
if let Some(s) = start {
if has_digit {
blocks.push(&input[s..i]);
}
}
start = Some(i); has_decimal = true; has_digit = false;
} else {
has_decimal = true;
}
}
_ => {
if let Some(s) = start {
if has_digit {
blocks.push(&input[s..i]);
}
}
start = None;
has_decimal = false;
has_digit = false;
}
}
}
if let Some(s) = start {
if has_digit {
blocks.push(&input[s..]);
}
}
blocks
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use crate::bbox::*;
use crate::parsing::parse_bbox;
#[test]
fn parse_bbox_simple() {
let string = r"[-180.0, -85.0, 180.0, 85.0]";
let bbox_result = parse_bbox(string);
let bbox = bbox_result.unwrap();
assert_eq!(bbox, BBox::new(-180.0, -85.0, 180.0, 85.0));
}
#[test]
fn parse_bbox_simple_len_5() {
let string = r#"[-180.0, -85.0, 180.0, 85.0, "uhhhhh"]"#;
let bbox_result = parse_bbox(string);
let bbox = bbox_result.unwrap();
assert_eq!(bbox, BBox::new(-180.0, -85.0, 180.0, 85.0));
}
#[test]
fn parse_bbox_simple_len_6() {
let string = r"[-180.0, -85.0, 180.0, 85.0, 0, 10]";
let bbox_result = parse_bbox(string);
let bbox = bbox_result.unwrap();
assert_eq!(bbox, BBox::new(-180.0, -85.0, 180.0, 85.0));
}
#[test]
fn parse_bbox_str_commas() {
let string = r"-180.0, -85.0, 180.0, 85.0";
let bbox_result = parse_bbox(string);
let bbox = bbox_result.unwrap();
assert_eq!(bbox, BBox::new(-180.0, -85.0, 180.0, 85.0));
}
#[test]
fn parse_bbox_str_spaces() {
let string = r"-180.0 -85.0 180.0 85.0";
let bbox_result = parse_bbox(string);
let bbox = bbox_result.unwrap();
assert_eq!(bbox, BBox::new(-180.0, -85.0, 180.0, 85.0));
}
#[test]
fn parse_bbox_from_coords() {
let string = "[-180.0, -85.0]";
let bbox_result = parse_bbox(string);
let bbox = bbox_result.unwrap();
assert_eq!(bbox, BBox::new(-180.0, -85.0, -180.0, -85.0));
}
#[test]
fn parse_bbox_bad() {
let string = r"[-180.0,]";
let bbox_result = parse_bbox(string);
assert!(bbox_result.is_err());
}
#[test]
fn parse_bbox_metadata_string() {
let s = "-176.696694,-14.373776,145.830505,71.341324";
let bbox = parse_bbox(s);
assert!(bbox.is_ok());
let bbox = bbox.unwrap();
assert_eq!(
bbox,
BBox::new(-176.696_694, -14.373_776, 145.830_505, 71.341_324)
);
}
}