Skip to content

Commit 798c4a6

Browse files
committed
wip
1 parent b286beb commit 798c4a6

File tree

6 files changed

+150
-67
lines changed

6 files changed

+150
-67
lines changed

.github/workflows/rust.yml

+7-29
Original file line numberDiff line numberDiff line change
@@ -8,45 +8,23 @@ on:
88
pull_request:
99
schedule: [cron: "45 6 * * *"]
1010

11-
env:
12-
RUST_TOOLCHAIN: stable
13-
TOOLCHAIN_PROFILE: default
14-
1511
name: Run tests
1612
jobs:
1713
lints:
1814
name: Run cargo fmt and cargo clippy
1915
runs-on: ubuntu-latest
2016
steps:
2117
- name: Checkout sources
22-
uses: actions/checkout@v2
23-
- name: Install toolchain
24-
uses: actions-rs/toolchain@v1
25-
with:
26-
profile: ${{ env.TOOLCHAIN_PROFILE }}
27-
toolchain: ${{ env.RUST_TOOLCHAIN }}
28-
override: true
29-
components: rustfmt, clippy
30-
- name: Cache
31-
uses: Swatinem/rust-cache@v1
18+
uses: actions/checkout@v3
19+
- name: Install fmt & clippy
20+
run: rustup component add clippy rustfmt
3221
- name: Run cargo fmt
33-
uses: actions-rs/cargo@v1
34-
with:
35-
command: fmt
36-
args: --all -- --check
22+
run: cargo fmt --all -- --check
3723
- name: Run cargo clippy
38-
uses: actions-rs/cargo@v1
39-
with:
40-
command: clippy
41-
args: -- -D warnings
24+
run: cargo clippy --all-targets --all-features -- -D warnings
4225
- name: Run cargo test
43-
uses: actions-rs/cargo@v1
44-
with:
45-
command: test
26+
run: cargo test
4627
- name: Run cargo docs
47-
uses: actions-rs/cargo@v1
28+
run: cargo doc --no-deps
4829
env:
4930
RUSTDOCFLAGS: -D warnings
50-
with:
51-
command: doc
52-
args: --no-deps

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
<a name="v0.3.2"></a>
12
### Pending
2-
*
3+
* Add `Bounds::try_from` now also supports `[f64; 4]`, `[i32; 4]`, `&[f64]`, `&[i32]` in addition to `Vec<f64>`
34

45
<a name="v0.3.1"></a>
56
### v0.3.1 (2022-05-29)

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "tilejson"
3-
version = "0.3.1"
3+
version = "0.3.2"
44
description = "Library for serializing the TileJSON file format"
55
authors = [
66
"Stepan Kuzmin <to.stepan.kuzmin@gmail.com>",

src/bounds.rs

+131-29
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::ParseBoundsError::{BadLen, ParseCoordError};
12
use serde_tuple::{Deserialize_tuple, Serialize_tuple};
23
use std::fmt::{Display, Formatter};
34
use std::num::ParseFloatError;
@@ -148,10 +149,48 @@ pub enum ParseBoundsError {
148149
impl Display for ParseBoundsError {
149150
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
150151
match self {
151-
ParseBoundsError::BadLen => {
152-
f.write_str("Incorrect number of values. Bounds expects four f64 values.")
153-
}
154-
ParseBoundsError::ParseCoordError(e) => e.fmt(f),
152+
BadLen => f.write_str("Incorrect number of values. Bounds expects four f64 values."),
153+
ParseCoordError(e) => e.fmt(f),
154+
}
155+
}
156+
}
157+
158+
impl From<[f64; 4]> for Bounds {
159+
/// Parse four f64 values as a Bounds value, same order as the [Bounds::new] constructor.
160+
///
161+
/// ```
162+
/// # use tilejson::Bounds;
163+
/// assert_eq!(
164+
/// Bounds::new(1., 2., 3., 4.),
165+
/// Bounds::from([1., 2., 3., 4.])
166+
/// );
167+
/// ```
168+
fn from(value: [f64; 4]) -> Self {
169+
Self {
170+
left: value[0],
171+
bottom: value[1],
172+
right: value[2],
173+
top: value[3],
174+
}
175+
}
176+
}
177+
178+
impl From<[i32; 4]> for Bounds {
179+
/// Parse four i32 values as a Bounds value, same order as the [Bounds::new] constructor.
180+
///
181+
/// ```
182+
/// # use tilejson::Bounds;
183+
/// assert_eq!(
184+
/// Bounds::new(1., 2., 3., 4.),
185+
/// Bounds::from([1, 2, 3, 4])
186+
/// );
187+
/// ```
188+
fn from(value: [i32; 4]) -> Self {
189+
Self {
190+
left: value[0] as f64,
191+
bottom: value[1] as f64,
192+
right: value[2] as f64,
193+
top: value[3] as f64,
155194
}
156195
}
157196
}
@@ -160,17 +199,53 @@ impl TryFrom<Vec<f64>> for Bounds {
160199
type Error = ParseBoundsError;
161200

162201
/// Parse four f64 values as a Bounds value, same order as the [Bounds::new] constructor.
202+
///
203+
/// ```
204+
/// # use tilejson::Bounds;
205+
/// assert_eq!(
206+
/// Bounds::new(1., 2., 3., 4.),
207+
/// Bounds::try_from(vec![1., 2., 3., 4.]).unwrap()
208+
/// );
209+
/// ```
163210
fn try_from(value: Vec<f64>) -> Result<Self, Self::Error> {
164-
if value.len() == 4 {
165-
Ok(Self {
166-
left: value[0],
167-
bottom: value[1],
168-
right: value[2],
169-
top: value[3],
170-
})
171-
} else {
172-
Err(ParseBoundsError::BadLen)
173-
}
211+
let arr: [f64; 4] = value.try_into().map_err(|_| BadLen)?;
212+
Ok(arr.into())
213+
}
214+
}
215+
216+
impl TryFrom<&[f64]> for Bounds {
217+
type Error = ParseBoundsError;
218+
219+
/// Parse four f64 values as a Bounds value, same order as the [Bounds::new] constructor.
220+
///
221+
/// ```
222+
/// # use tilejson::Bounds;
223+
/// assert_eq!(
224+
/// Bounds::new(1., 2., 3., 4.),
225+
/// Bounds::try_from(vec![1., 2., 3., 4.].as_slice()).unwrap()
226+
/// );
227+
/// ```
228+
fn try_from(value: &[f64]) -> Result<Self, Self::Error> {
229+
let arr: [f64; 4] = value.try_into().map_err(|_| BadLen)?;
230+
Ok(arr.into())
231+
}
232+
}
233+
234+
impl TryFrom<&[i32]> for Bounds {
235+
type Error = ParseBoundsError;
236+
237+
/// Parse four i32 values as a Bounds value, same order as the [Bounds::new] constructor.
238+
///
239+
/// ```
240+
/// # use tilejson::Bounds;
241+
/// assert_eq!(
242+
/// Bounds::new(1., 2., 3., 4.),
243+
/// Bounds::try_from(vec![1, 2, 3, 4].as_slice()).unwrap()
244+
/// );
245+
/// ```
246+
fn try_from(value: &[i32]) -> Result<Self, Self::Error> {
247+
let arr: [i32; 4] = value.try_into().map_err(|_| BadLen)?;
248+
Ok(arr.into())
174249
}
175250
}
176251

@@ -188,28 +263,26 @@ impl FromStr for Bounds {
188263
/// assert_eq!(bounds, Bounds::new(-1.0, -2.0, 3.0, 4.0));
189264
/// ```
190265
fn from_str(s: &str) -> Result<Self, Self::Err> {
191-
let mut vals = s.split(',').map(|s| s.trim());
192-
let mut next_val = || {
193-
vals.next().map_or(Err(ParseBoundsError::BadLen), |v| {
194-
v.parse().map_err(ParseBoundsError::ParseCoordError)
195-
})
196-
};
197-
let bounds = Self {
198-
left: next_val()?,
199-
bottom: next_val()?,
200-
right: next_val()?,
201-
top: next_val()?,
202-
};
203-
match vals.next() {
204-
Some(_) => Err(ParseBoundsError::BadLen),
205-
None => Ok(bounds),
266+
let mut values = s.split(',');
267+
let mut result = [0.; 4];
268+
for i in 0..4 {
269+
result[i] = values
270+
.next()
271+
.ok_or(ParseBoundsError::BadLen)?
272+
.trim()
273+
.parse()
274+
.map_err(ParseBoundsError::ParseCoordError)?;
206275
}
276+
values
277+
.next()
278+
.map_or(Ok(result.into()), |_| Err(ParseBoundsError::BadLen))
207279
}
208280
}
209281

210282
#[cfg(test)]
211283
mod tests {
212284
use super::*;
285+
use crate::ParseBoundsError::BadLen;
213286

214287
#[test]
215288
fn test_parse_err() {
@@ -232,4 +305,33 @@ mod tests {
232305
assert_eq!(val("0,0,0,0"), Bounds::new(0.0, 0.0, 0.0, 0.0));
233306
assert_eq!(val(" 1 ,2.0, 3.0, 4.0 "), Bounds::new(1.0, 2.0, 3.0, 4.0));
234307
}
308+
309+
#[test]
310+
fn test_parse_errors() {
311+
let err = |s| Bounds::from_str(s).unwrap_err();
312+
assert_eq!(err("0,0,0"), BadLen);
313+
assert_eq!(err("0,0,0,0,0"), BadLen);
314+
assert!(matches!(err(""), ParseCoordError(_)));
315+
assert!(matches!(err("a"), ParseCoordError(_)));
316+
assert!(matches!(err("0,0,0,1a"), ParseCoordError(_)));
317+
}
318+
319+
#[test]
320+
fn test_from() -> Result<(), ParseBoundsError> {
321+
let exp = Bounds::new(1.0, 2.0, 3.0, 4.0);
322+
assert_eq!(exp, Bounds::from([1.0, 2.0, 3.0, 4.0]));
323+
assert_eq!(exp, Bounds::try_from([1.0, 2.0, 3.0, 4.0].as_slice())?);
324+
assert_eq!(exp, Bounds::try_from(vec![1.0, 2.0, 3.0, 4.0])?);
325+
let val = vec![1.0, 2.0, 3.0, 4.0];
326+
assert_eq!(exp, Bounds::try_from((&val).as_slice())?);
327+
assert_eq!(exp, Bounds::try_from(val.as_slice())?);
328+
329+
// i32
330+
assert_eq!(exp, Bounds::from([1, 2, 3, 4]));
331+
assert_eq!(exp, Bounds::try_from([1, 2, 3, 4].as_slice())?);
332+
let val = vec![1, 2, 3, 4];
333+
assert_eq!(exp, Bounds::try_from((&val).as_slice())?);
334+
assert_eq!(exp, Bounds::try_from(val.as_slice())?);
335+
Ok(())
336+
}
235337
}

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
//! Use [tilejson!] macro to instantiate a valid [TileJSON].
88
//! Use [TileJSON::set_missing_defaults] to populate default values per spec.
99
10+
extern crate core;
11+
1012
mod bounds;
1113
mod center;
1214
mod tilejson;

src/tilejson.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ mod tests {
315315
]
316316
}"#;
317317

318-
let mut tilejson: TileJSON = serde_json::from_str(&tilejson_str).unwrap();
318+
let mut tilejson: TileJSON = serde_json::from_str(tilejson_str).unwrap();
319319

320320
assert_eq!(
321321
tilejson,
@@ -388,11 +388,11 @@ mod tests {
388388

389389
#[test]
390390
fn test_bad_json() {
391-
parse(&r#"{"tilejson":"3.0.0", "tiles":["x"], "center":[]}"#).unwrap_err();
392-
parse(&r#"{"tilejson":"3.0.0", "tiles":["x"], "center":[1,2]}"#).unwrap_err();
393-
parse(&r#"{"tilejson":"3.0.0", "tiles":["x"], "center":[1,2,3,4]}"#).unwrap_err();
394-
parse(&r#"{"tilejson":"3.0.0", "tiles":["x"], "bounds":[]}"#).unwrap_err();
395-
parse(&r#"{"tilejson":"3.0.0", "tiles":["x"], "bounds":[1,2,3]}"#).unwrap_err();
396-
parse(&r#"{"tilejson":"3.0.0", "tiles":["x"], "bounds":[1,2,3,4,5]}"#).unwrap_err();
391+
parse(r#"{"tilejson":"3.0.0", "tiles":["x"], "center":[]}"#).unwrap_err();
392+
parse(r#"{"tilejson":"3.0.0", "tiles":["x"], "center":[1,2]}"#).unwrap_err();
393+
parse(r#"{"tilejson":"3.0.0", "tiles":["x"], "center":[1,2,3,4]}"#).unwrap_err();
394+
parse(r#"{"tilejson":"3.0.0", "tiles":["x"], "bounds":[]}"#).unwrap_err();
395+
parse(r#"{"tilejson":"3.0.0", "tiles":["x"], "bounds":[1,2,3]}"#).unwrap_err();
396+
parse(r#"{"tilejson":"3.0.0", "tiles":["x"], "bounds":[1,2,3,4,5]}"#).unwrap_err();
397397
}
398398
}

0 commit comments

Comments
 (0)