diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 03d3372..91bffaf 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -8,10 +8,6 @@ on:
pull_request:
schedule: [cron: "45 6 * * *"]
-env:
- RUST_TOOLCHAIN: stable
- TOOLCHAIN_PROFILE: default
-
name: Run tests
jobs:
lints:
@@ -19,34 +15,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
- uses: actions/checkout@v2
- - name: Install toolchain
- uses: actions-rs/toolchain@v1
- with:
- profile: ${{ env.TOOLCHAIN_PROFILE }}
- toolchain: ${{ env.RUST_TOOLCHAIN }}
- override: true
- components: rustfmt, clippy
- - name: Cache
- uses: Swatinem/rust-cache@v1
+ uses: actions/checkout@v3
+ - name: Install fmt & clippy
+ run: rustup component add clippy rustfmt
- name: Run cargo fmt
- uses: actions-rs/cargo@v1
- with:
- command: fmt
- args: --all -- --check
+ run: cargo fmt --all -- --check
- name: Run cargo clippy
- uses: actions-rs/cargo@v1
- with:
- command: clippy
- args: -- -D warnings
+ run: cargo clippy --all-targets --all-features -- -D warnings
- name: Run cargo test
- uses: actions-rs/cargo@v1
- with:
- command: test
+ run: cargo test
- name: Run cargo docs
- uses: actions-rs/cargo@v1
+ run: cargo doc --no-deps
env:
RUSTDOCFLAGS: -D warnings
- with:
- command: doc
- args: --no-deps
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 276c067..1137b16 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,7 @@
+
### Pending
-*
+* Add `Bounds::from` for `[f64; 4]`, `[f32; 4]`, `[i32; 4]`
+* Add `Bounds::try_from` now also supports `&[f64]`, `&[f32]`, `&[i32]` in addition to `Vec`
### v0.3.1 (2022-05-29)
diff --git a/Cargo.toml b/Cargo.toml
index 23c87d8..7565541 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tilejson"
-version = "0.3.1"
+version = "0.3.2"
description = "Library for serializing the TileJSON file format"
authors = [
"Stepan Kuzmin ",
diff --git a/src/bounds.rs b/src/bounds.rs
index ab303d7..fc70506 100644
--- a/src/bounds.rs
+++ b/src/bounds.rs
@@ -1,3 +1,4 @@
+use crate::ParseBoundsError::{BadLen, ParseCoordError};
use serde_tuple::{Deserialize_tuple, Serialize_tuple};
use std::fmt::{Display, Formatter};
use std::num::ParseFloatError;
@@ -148,10 +149,68 @@ pub enum ParseBoundsError {
impl Display for ParseBoundsError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
- ParseBoundsError::BadLen => {
- f.write_str("Incorrect number of values. Bounds expects four f64 values.")
- }
- ParseBoundsError::ParseCoordError(e) => e.fmt(f),
+ BadLen => f.write_str("Incorrect number of values. Bounds expects four f64 values."),
+ ParseCoordError(e) => e.fmt(f),
+ }
+ }
+}
+
+impl From<[f64; 4]> for Bounds {
+ /// Parse four f64 values as a Bounds value, same order as the [Bounds::new] constructor.
+ ///
+ /// ```
+ /// # use tilejson::Bounds;
+ /// assert_eq!(
+ /// Bounds::new(1., 2., 3., 4.),
+ /// Bounds::from([1., 2., 3., 4.])
+ /// );
+ /// ```
+ fn from(value: [f64; 4]) -> Self {
+ Self {
+ left: value[0],
+ bottom: value[1],
+ right: value[2],
+ top: value[3],
+ }
+ }
+}
+
+impl From<[f32; 4]> for Bounds {
+ /// Parse four f32 values as a Bounds value, same order as the [Bounds::new] constructor.
+ ///
+ /// ```
+ /// # use tilejson::Bounds;
+ /// assert_eq!(
+ /// Bounds::new(1., 2., 3., 4.),
+ /// Bounds::from([1.0f32, 2.0f32, 3.0f32, 4.0f32])
+ /// );
+ /// ```
+ fn from(value: [f32; 4]) -> Self {
+ Self {
+ left: value[0] as f64,
+ bottom: value[1] as f64,
+ right: value[2] as f64,
+ top: value[3] as f64,
+ }
+ }
+}
+
+impl From<[i32; 4]> for Bounds {
+ /// Parse four i32 values as a Bounds value, same order as the [Bounds::new] constructor.
+ ///
+ /// ```
+ /// # use tilejson::Bounds;
+ /// assert_eq!(
+ /// Bounds::new(1., 2., 3., 4.),
+ /// Bounds::from([1, 2, 3, 4])
+ /// );
+ /// ```
+ fn from(value: [i32; 4]) -> Self {
+ Self {
+ left: value[0] as f64,
+ bottom: value[1] as f64,
+ right: value[2] as f64,
+ top: value[3] as f64,
}
}
}
@@ -160,17 +219,71 @@ impl TryFrom> for Bounds {
type Error = ParseBoundsError;
/// Parse four f64 values as a Bounds value, same order as the [Bounds::new] constructor.
+ ///
+ /// ```
+ /// # use tilejson::Bounds;
+ /// assert_eq!(
+ /// Bounds::new(1., 2., 3., 4.),
+ /// Bounds::try_from(vec![1., 2., 3., 4.]).unwrap()
+ /// );
+ /// ```
fn try_from(value: Vec) -> Result {
- if value.len() == 4 {
- Ok(Self {
- left: value[0],
- bottom: value[1],
- right: value[2],
- top: value[3],
- })
- } else {
- Err(ParseBoundsError::BadLen)
- }
+ let arr: [f64; 4] = value.try_into().map_err(|_| BadLen)?;
+ Ok(arr.into())
+ }
+}
+
+impl TryFrom<&[f64]> for Bounds {
+ type Error = ParseBoundsError;
+
+ /// Parse four f64 values as a Bounds value, same order as the [Bounds::new] constructor.
+ ///
+ /// ```
+ /// # use tilejson::Bounds;
+ /// assert_eq!(
+ /// Bounds::new(1., 2., 3., 4.),
+ /// Bounds::try_from(vec![1., 2., 3., 4.].as_slice()).unwrap()
+ /// );
+ /// ```
+ fn try_from(value: &[f64]) -> Result {
+ let arr: [f64; 4] = value.try_into().map_err(|_| BadLen)?;
+ Ok(arr.into())
+ }
+}
+
+impl TryFrom<&[f32]> for Bounds {
+ type Error = ParseBoundsError;
+
+ /// Parse four f32 values as a Bounds value, same order as the [Bounds::new] constructor.
+ ///
+ /// ```
+ /// # use tilejson::Bounds;
+ /// assert_eq!(
+ /// Bounds::new(1., 2., 3., 4.),
+ /// Bounds::try_from(vec![1.0f32, 2.0f32, 3.0f32, 4.0f32].as_slice()).unwrap()
+ /// );
+ /// ```
+ fn try_from(value: &[f32]) -> Result {
+ let arr: [f32; 4] = value.try_into().map_err(|_| BadLen)?;
+ Ok(arr.into())
+ }
+}
+
+impl TryFrom<&[i32]> for Bounds {
+ type Error = ParseBoundsError;
+
+ /// Parse four i32 values as a Bounds value, same order as the [Bounds::new] constructor.
+ ///
+ /// ```
+ /// # use tilejson::Bounds;
+ /// assert_eq!(
+ /// Bounds::new(1., 2., 3., 4.),
+ /// Bounds::try_from(vec![1, 2, 3, 4].as_slice()).unwrap()
+ /// );
+ /// ```
+ fn try_from(value: &[i32]) -> Result {
+ let arr: [i32; 4] = value.try_into().map_err(|_| BadLen)?;
+ Ok(arr.into())
}
}
@@ -188,28 +301,26 @@ impl FromStr for Bounds {
/// assert_eq!(bounds, Bounds::new(-1.0, -2.0, 3.0, 4.0));
/// ```
fn from_str(s: &str) -> Result {
- let mut vals = s.split(',').map(|s| s.trim());
- let mut next_val = || {
- vals.next().map_or(Err(ParseBoundsError::BadLen), |v| {
- v.parse().map_err(ParseBoundsError::ParseCoordError)
- })
- };
- let bounds = Self {
- left: next_val()?,
- bottom: next_val()?,
- right: next_val()?,
- top: next_val()?,
- };
- match vals.next() {
- Some(_) => Err(ParseBoundsError::BadLen),
- None => Ok(bounds),
+ let mut values = s.split(',');
+ let mut result = [0.; 4];
+ for i in 0..4 {
+ result[i] = values
+ .next()
+ .ok_or(ParseBoundsError::BadLen)?
+ .trim()
+ .parse()
+ .map_err(ParseBoundsError::ParseCoordError)?;
}
+ values
+ .next()
+ .map_or(Ok(result.into()), |_| Err(ParseBoundsError::BadLen))
}
}
#[cfg(test)]
mod tests {
use super::*;
+ use crate::ParseBoundsError::BadLen;
#[test]
fn test_parse_err() {
@@ -232,4 +343,41 @@ mod tests {
assert_eq!(val("0,0,0,0"), Bounds::new(0.0, 0.0, 0.0, 0.0));
assert_eq!(val(" 1 ,2.0, 3.0, 4.0 "), Bounds::new(1.0, 2.0, 3.0, 4.0));
}
+
+ #[test]
+ fn test_parse_errors() {
+ let err = |s| Bounds::from_str(s).unwrap_err();
+ assert_eq!(err("0,0,0"), BadLen);
+ assert_eq!(err("0,0,0,0,0"), BadLen);
+ assert!(matches!(err(""), ParseCoordError(_)));
+ assert!(matches!(err("a"), ParseCoordError(_)));
+ assert!(matches!(err("0,0,0,1a"), ParseCoordError(_)));
+ }
+
+ #[test]
+ fn test_from() -> Result<(), ParseBoundsError> {
+ let exp = Bounds::new(1.0, 2.0, 3.0, 4.0);
+ assert_eq!(exp, Bounds::from([1.0, 2.0, 3.0, 4.0]));
+ assert_eq!(exp, Bounds::try_from([1.0, 2.0, 3.0, 4.0].as_slice())?);
+ assert_eq!(exp, Bounds::try_from(vec![1.0, 2.0, 3.0, 4.0])?);
+ let val = vec![1.0, 2.0, 3.0, 4.0];
+ assert_eq!(exp, Bounds::try_from((&val).as_slice())?);
+ assert_eq!(exp, Bounds::try_from(val.as_slice())?);
+
+ // f32
+ assert_eq!(exp, Bounds::from([1.0f32, 2.0f32, 3.0f32, 4.0f32]));
+ let val_array = [1.0f32, 2.0f32, 3.0f32, 4.0f32];
+ assert_eq!(exp, Bounds::try_from(val_array.as_slice())?);
+ let val = vec![1.0f32, 2.0f32, 3.0f32, 4.0f32];
+ assert_eq!(exp, Bounds::try_from((&val).as_slice())?);
+ assert_eq!(exp, Bounds::try_from(val.as_slice())?);
+
+ // i32
+ assert_eq!(exp, Bounds::from([1, 2, 3, 4]));
+ assert_eq!(exp, Bounds::try_from([1, 2, 3, 4].as_slice())?);
+ let val = vec![1, 2, 3, 4];
+ assert_eq!(exp, Bounds::try_from((&val).as_slice())?);
+ assert_eq!(exp, Bounds::try_from(val.as_slice())?);
+ Ok(())
+ }
}
diff --git a/src/tilejson.rs b/src/tilejson.rs
index cc543c8..c751fa5 100644
--- a/src/tilejson.rs
+++ b/src/tilejson.rs
@@ -315,7 +315,7 @@ mod tests {
]
}"#;
- let mut tilejson: TileJSON = serde_json::from_str(&tilejson_str).unwrap();
+ let mut tilejson: TileJSON = serde_json::from_str(tilejson_str).unwrap();
assert_eq!(
tilejson,
@@ -388,11 +388,11 @@ mod tests {
#[test]
fn test_bad_json() {
- parse(&r#"{"tilejson":"3.0.0", "tiles":["x"], "center":[]}"#).unwrap_err();
- parse(&r#"{"tilejson":"3.0.0", "tiles":["x"], "center":[1,2]}"#).unwrap_err();
- parse(&r#"{"tilejson":"3.0.0", "tiles":["x"], "center":[1,2,3,4]}"#).unwrap_err();
- parse(&r#"{"tilejson":"3.0.0", "tiles":["x"], "bounds":[]}"#).unwrap_err();
- parse(&r#"{"tilejson":"3.0.0", "tiles":["x"], "bounds":[1,2,3]}"#).unwrap_err();
- parse(&r#"{"tilejson":"3.0.0", "tiles":["x"], "bounds":[1,2,3,4,5]}"#).unwrap_err();
+ parse(r#"{"tilejson":"3.0.0", "tiles":["x"], "center":[]}"#).unwrap_err();
+ parse(r#"{"tilejson":"3.0.0", "tiles":["x"], "center":[1,2]}"#).unwrap_err();
+ parse(r#"{"tilejson":"3.0.0", "tiles":["x"], "center":[1,2,3,4]}"#).unwrap_err();
+ parse(r#"{"tilejson":"3.0.0", "tiles":["x"], "bounds":[]}"#).unwrap_err();
+ parse(r#"{"tilejson":"3.0.0", "tiles":["x"], "bounds":[1,2,3]}"#).unwrap_err();
+ parse(r#"{"tilejson":"3.0.0", "tiles":["x"], "bounds":[1,2,3,4,5]}"#).unwrap_err();
}
}