slint_mapping/sources/
file.rs1use crate::source::{TileKey, TileSource};
10use std::path::PathBuf;
11
12pub struct FileTileSource {
22 root: PathBuf,
23 extension: String,
24 tile_size: u32,
25 min_zoom: u8,
26 max_zoom: u8,
27}
28
29impl FileTileSource {
30 pub fn new(root: impl Into<PathBuf>) -> Self {
31 Self {
32 root: root.into(),
33 extension: "png".to_string(),
34 tile_size: 256,
35 min_zoom: 0,
36 max_zoom: 22,
37 }
38 }
39
40 pub fn with_extension(mut self, ext: impl Into<String>) -> Self {
42 self.extension = ext.into();
43 self
44 }
45
46 pub fn with_tile_size(mut self, size: u32) -> Self {
48 self.tile_size = size;
49 self
50 }
51
52 pub fn with_zoom_range(mut self, min: u8, max: u8) -> Self {
56 self.min_zoom = min;
57 self.max_zoom = max;
58 self
59 }
60}
61
62impl TileSource for FileTileSource {
63 fn tile(&self, key: TileKey) -> Option<slint::Image> {
64 let path = self
66 .root
67 .join(key.z.to_string())
68 .join(key.x.to_string())
69 .join(format!("{}.{}", key.y, self.extension));
70 if !path.exists() {
75 return None;
76 }
77 slint::Image::load_from_path(&path).ok()
78 }
79
80 fn tile_size(&self) -> u32 {
81 self.tile_size
82 }
83
84 fn min_zoom(&self) -> u8 {
85 self.min_zoom
86 }
87
88 fn max_zoom(&self) -> u8 {
89 self.max_zoom
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 #[test]
98 fn missing_tile_returns_none() {
99 let src = FileTileSource::new("/definitely-does-not-exist-9b3a2c");
100 assert!(src.tile(TileKey { x: 0, y: 0, z: 0 }).is_none());
101 }
102
103 #[test]
104 fn sample_bundle_serves_world_tile() {
105 let src = FileTileSource::new(crate::SAMPLE_TILES_DIR);
108 assert!(
109 src.tile(TileKey { x: 0, y: 0, z: 0 }).is_some(),
110 "sample bundle should serve the (0,0,0) world tile — \
111 did the bundle move or is the SAMPLE_TILES_DIR path wrong?"
112 );
113 }
114
115 #[test]
116 fn sample_bundle_serves_every_zoom_3_tile() {
117 let src = FileTileSource::new(crate::SAMPLE_TILES_DIR);
120 for x in 0..8u32 {
121 for y in 0..8u32 {
122 assert!(
123 src.tile(TileKey { x, y, z: 3 }).is_some(),
124 "missing sample tile (x={x}, y={y}, z=3)"
125 );
126 }
127 }
128 }
129
130 #[test]
131 fn zoom_range_overridable() {
132 let src = FileTileSource::new("/tmp").with_zoom_range(5, 14);
133 assert_eq!(src.min_zoom(), 5);
134 assert_eq!(src.max_zoom(), 14);
135 }
136}