use crate::error::{RenderError, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TileCoordinate {
pub z: u8,
pub x: u32,
pub y: u32,
}
impl TileCoordinate {
pub fn new(z: u8, x: u32, y: u32) -> Result<Self> {
let max_coord = 2u32.pow(z as u32);
if x >= max_coord || y >= max_coord {
return Err(RenderError::InvalidCoordinate(format!(
"Tile coordinate ({}, {}) out of range for zoom level {} (max: {})",
x, y, z, max_coord - 1
)));
}
Ok(Self { z, x, y })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BackgroundColor {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
impl BackgroundColor {
pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
Self { r, g, b, a }
}
pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
Self::new(r, g, b, 255)
}
pub const fn transparent() -> Self {
Self::new(0, 0, 0, 0)
}
pub const fn white() -> Self {
Self::rgb(255, 255, 255)
}
pub const fn black() -> Self {
Self::rgb(0, 0, 0)
}
}
impl Default for BackgroundColor {
fn default() -> Self {
Self::transparent()
}
}
#[derive(Debug, Clone)]
pub struct Settings {
pub background_color: BackgroundColor,
pub size: u32,
}
impl Settings {
pub fn new() -> Self {
Self::default()
}
pub fn builder() -> SettingsBuilder {
SettingsBuilder::default()
}
}
impl Default for Settings {
fn default() -> Self {
Self {
background_color: BackgroundColor::default(),
size: 256,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct SettingsBuilder {
background_color: Option<BackgroundColor>,
size: Option<u32>,
}
impl SettingsBuilder {
pub fn background_color(mut self, color: BackgroundColor) -> Self {
self.background_color = Some(color);
self
}
pub fn size(mut self, size: u32) -> Self {
self.size = Some(size);
self
}
pub fn build(self) -> Result<Settings> {
let size = self.size.unwrap_or(256);
if size == 0 {
return Err(RenderError::InvalidSettings(
"Tile size must be greater than 0".to_string(),
));
}
if size > 4096 {
return Err(RenderError::InvalidSettings(
"Tile size must be 4096 or less".to_string(),
));
}
Ok(Settings {
background_color: self.background_color.unwrap_or_default(),
size,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tile_coordinate_new() {
assert!(TileCoordinate::new(0, 0, 0).is_ok());
assert!(TileCoordinate::new(5, 15, 20).is_ok());
assert!(TileCoordinate::new(10, 1023, 1023).is_ok());
assert!(TileCoordinate::new(0, 1, 0).is_err()); assert!(TileCoordinate::new(5, 32, 0).is_err()); assert!(TileCoordinate::new(5, 0, 32).is_err());
}
#[test]
fn test_background_color() {
let color = BackgroundColor::new(255, 128, 64, 200);
assert_eq!(color.r, 255);
assert_eq!(color.g, 128);
assert_eq!(color.b, 64);
assert_eq!(color.a, 200);
let rgb = BackgroundColor::rgb(100, 150, 200);
assert_eq!(rgb.a, 255);
let transparent = BackgroundColor::transparent();
assert_eq!(transparent.a, 0);
}
#[test]
fn test_settings_builder() {
let settings = Settings::builder()
.size(512)
.background_color(BackgroundColor::white())
.build()
.unwrap();
assert_eq!(settings.size, 512);
assert_eq!(settings.background_color, BackgroundColor::white());
}
#[test]
fn test_settings_builder_defaults() {
let settings = Settings::builder().build().unwrap();
assert_eq!(settings.size, 256);
assert_eq!(settings.background_color, BackgroundColor::transparent());
}
#[test]
fn test_settings_validation() {
assert!(Settings::builder().size(0).build().is_err());
assert!(Settings::builder().size(4097).build().is_err());
assert!(Settings::builder().size(1).build().is_ok());
assert!(Settings::builder().size(256).build().is_ok());
assert!(Settings::builder().size(4096).build().is_ok());
}
}