use std::path::PathBuf;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TileSpecification {
level: u8,
x: u32,
y: u32,
}
impl TileSpecification {
pub fn new(level: u8, x: u32, y: u32) -> Self {
assert!(level < 29, "Level is too large to be represented");
assert!(x < (1 << 29) && y < (1 << 29), "Coordinate out of range");
TileSpecification { level, x, y }
}
pub fn level(&self) -> u8 {
self.level
}
pub fn x(&self) -> u32 {
self.x
}
pub fn y(&self) -> u32 {
self.y
}
pub fn filename(&self) -> PathBuf {
format!("{}/{}/{:016x}.png", self.level, self.x, u64::from(*self)).into()
}
pub fn get_partial_url(&self) -> String {
format!("{}/{}/{}.png", self.level, self.x, self.y)
}
}
impl From<u64> for TileSpecification {
fn from(item: u64) -> Self {
let level = (item >> 58) as u8;
let y = ((item >> 29) & 0x1FFF_FFFF) as u32;
let x = (item & 0x1FFF_FFFF) as u32;
Self { level, y, x }
}
}
impl From<TileSpecification> for u64 {
fn from(item: TileSpecification) -> u64 {
(item.level as u64) << 58 | (item.y as u64) << 29 | item.x as u64
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::{prop_assert_eq, proptest};
#[test]
fn base_test() {
let spec = TileSpecification::new(1, 2, 3);
let new_spec = TileSpecification::from(u64::from(spec));
assert_eq!(spec, new_spec);
}
#[test]
fn name_test() {
let spec = TileSpecification::new(0, 10, 0);
assert_eq!(
spec.filename().to_str().unwrap(),
"0/10/000000000000000a.png"
);
let spec = TileSpecification::new(1, 2, 3);
assert_eq!(spec.get_partial_url(), "1/2/3.png");
}
proptest! {
#[test]
fn conversion(scale in (0u8..10), x in (0u32..100), y in (0u32..100)) {
let spec = TileSpecification::new(scale, x, y);
let code = u64::from(spec);
let reverse_spec = TileSpecification::from(code);
prop_assert_eq!(spec, reverse_spec);
}
}
}