use std::cell::Cell;
use geopackage::{
BoundingBox, FeatureWriter, GeoPackage, Layer, Tile, TileCursor, TilePyramid, TileStream,
};
pub struct Container {
gpkg: Box<GeoPackage>,
children: Cell<usize>,
}
impl Container {
pub fn new(gpkg: GeoPackage) -> Self {
Self {
gpkg: Box::new(gpkg),
children: Cell::new(0),
}
}
pub fn gpkg(&self) -> &GeoPackage {
&self.gpkg
}
pub fn layer(&self, name: &str) -> geopackage::Result<LayerHandle> {
let layer = self.gpkg.layer(name)?;
Ok(self.adopt(layer))
}
pub fn attributes(&self, name: &str) -> geopackage::Result<LayerHandle> {
let layer = self.gpkg.attributes(name)?;
Ok(self.adopt(layer))
}
pub fn layer_with_columns(
&self,
name: &str,
columns: &[&str],
attributes: bool,
) -> geopackage::Result<LayerHandle> {
let layer = if attributes {
self.gpkg.attributes(name)?
} else {
self.gpkg.layer(name)?
};
Ok(self.adopt(layer.with_columns(columns)?))
}
fn adopt(&self, layer: Layer<'_>) -> LayerHandle {
let erased: Layer<'static> = unsafe { std::mem::transmute(layer) };
LayerHandle {
layer: erased,
_token: self.token(),
}
}
pub fn tiles(&self, name: &str) -> geopackage::Result<TilesHandle> {
let pyramid = self.gpkg.tiles(name)?;
let erased: TilePyramid<'static> = unsafe { std::mem::transmute(pyramid) };
Ok(TilesHandle {
pyramid: erased,
_token: self.token(),
})
}
pub fn create_tiles(
&self,
builder: &geopackage::TilePyramidBuilder,
) -> geopackage::Result<TilesHandle> {
let pyramid = self.gpkg.create_tile_pyramid(builder)?;
let erased: TilePyramid<'static> = unsafe { std::mem::transmute(pyramid) };
Ok(TilesHandle {
pyramid: erased,
_token: self.token(),
})
}
pub fn token(&self) -> ChildToken {
self.children.set(self.children.get() + 1);
ChildToken {
parent: std::ptr::from_ref(self),
}
}
fn release_child(&self) {
self.children.set(self.children.get().saturating_sub(1));
}
pub fn outstanding_children(&self) -> usize {
self.children.get()
}
pub fn close(self) -> geopackage::Result<()> {
self.gpkg.close()
}
}
pub struct ChildToken {
parent: *const Container,
}
impl Drop for ChildToken {
fn drop(&mut self) {
let parent = unsafe { &*self.parent };
parent.release_child();
}
}
pub struct LayerHandle {
layer: Layer<'static>,
_token: ChildToken,
}
impl LayerHandle {
pub fn layer(&self) -> &Layer<'static> {
&self.layer
}
pub fn writer(&self) -> geopackage::Result<WriterHandle> {
let writer = self.layer.writer()?;
Ok(WriterHandle {
writer,
_token: self.token(),
})
}
pub fn token(&self) -> ChildToken {
let parent = unsafe { &*self._token.parent };
parent.token()
}
}
pub struct WriterHandle {
writer: FeatureWriter<'static>,
_token: ChildToken,
}
impl WriterHandle {
pub fn writer_mut(&mut self) -> &mut FeatureWriter<'static> {
&mut self.writer
}
pub fn commit(self) -> geopackage::Result<()> {
self.writer.commit()
}
}
pub struct TilesHandle {
pyramid: TilePyramid<'static>,
_token: ChildToken,
}
pub enum CursorScan {
All,
At(i64),
In(i64, BoundingBox),
}
impl TilesHandle {
pub fn pyramid(&self) -> &TilePyramid<'static> {
&self.pyramid
}
pub fn token(&self) -> ChildToken {
let parent = unsafe { &*self._token.parent };
parent.token()
}
pub fn cursor(&self, scan: &CursorScan) -> geopackage::Result<TileCursorHandle> {
let cursor = match scan {
CursorScan::All => self.pyramid.cursor()?,
CursorScan::At(zoom) => self.pyramid.cursor_at(*zoom)?,
CursorScan::In(zoom, bbox) => self.pyramid.cursor_in(*zoom, *bbox)?,
};
let erased: TileCursor<'static> = unsafe { std::mem::transmute(cursor) };
let mut cursor = Box::new(erased);
let stream = cursor.tiles()?;
let stream: TileStream<'static> = unsafe { std::mem::transmute(stream) };
Ok(TileCursorHandle {
stream,
_cursor: cursor,
_token: self.token(),
})
}
}
pub struct TileCursorHandle {
stream: TileStream<'static>,
_cursor: Box<TileCursor<'static>>,
_token: ChildToken,
}
impl TileCursorHandle {
#[expect(
clippy::should_implement_trait,
reason = "a lending cursor cannot implement Iterator: its item borrows the iterator. The name matches TileStream::next, whose expectation records the same reasoning"
)]
pub fn next(&mut self) -> geopackage::Result<Option<Tile<'_>>> {
self.stream.next()
}
}