use std::{
cmp,
ffi::{c_void, CStr, CString},
fmt::Debug,
ptr,
};
use chrono::{DateTime, TimeDelta, TimeZone, Utc};
#[cfg(feature = "geos")]
use geos::{Geom, Geometry};
use crate::{
collections::{
base::{
Span, {impl_collection, Collection},
},
datetime::TsTzSpan,
},
errors::ParseError,
utils::{create_interval, from_meos_timestamp, to_meos_timestamp},
WKBVariant,
};
use super::r#box::Box as MeosBox;
pub struct STBox {
_inner: ptr::NonNull<meos_sys::STBox>,
}
impl MeosBox for STBox {
fn from_wkb(wkb: &[u8]) -> Self {
unsafe { Self::from_inner(meos_sys::stbox_from_wkb(wkb.as_ptr(), wkb.len())) }
}
fn from_hexwkb(hexwkb: &[u8]) -> Self {
let c_hexwkb = CString::new(hexwkb).unwrap();
unsafe {
let inner = meos_sys::stbox_from_hexwkb(c_hexwkb.as_ptr());
Self::from_inner(inner)
}
}
fn from_temporal_span(span: TsTzSpan) -> Self {
unsafe { Self::from_inner(meos_sys::tstzspan_to_stbox(span.inner())) }
}
fn from_time<Tz: TimeZone>(time: DateTime<Tz>) -> Self {
let timestamptz = to_meos_timestamp(&time);
unsafe { Self::from_inner(meos_sys::timestamptz_to_stbox(timestamptz)) }
}
fn tstzspan(&self) -> TsTzSpan {
unsafe { TsTzSpan::from_inner(meos_sys::stbox_to_tstzspan(self.inner())) }
}
fn as_wkb(&self, variant: WKBVariant) -> &[u8] {
unsafe {
let mut size: usize = 0;
let ptr = meos_sys::stbox_as_wkb(self.inner(), variant.into(), &raw mut size);
std::slice::from_raw_parts(ptr, size)
}
}
fn as_hexwkb(&self, variant: WKBVariant) -> &[u8] {
unsafe {
let mut size: usize = 0;
let hexwkb_ptr = meos_sys::stbox_as_hexwkb(self.inner(), variant.into(), &raw mut size);
CStr::from_ptr(hexwkb_ptr).to_bytes()
}
}
fn has_x(&self) -> bool {
unsafe { meos_sys::stbox_hasx(self.inner()) }
}
fn has_t(&self) -> bool {
unsafe { meos_sys::stbox_hast(self.inner()) }
}
fn xmin(&self) -> Option<f64> {
unsafe {
let mut value = 0.0;
let ptr: *mut f64 = ptr::addr_of_mut!(value);
if meos_sys::stbox_xmin(self.inner(), ptr) {
Some(value)
} else {
None
}
}
}
fn xmax(&self) -> Option<f64> {
unsafe {
let mut value = 0.0;
let ptr: *mut f64 = ptr::addr_of_mut!(value);
if meos_sys::stbox_xmax(self.inner(), ptr) {
Some(value)
} else {
None
}
}
}
fn tmin(&self) -> Option<DateTime<Utc>> {
unsafe {
let mut value: i64 = 0;
let ptr: *mut i64 = ptr::addr_of_mut!(value);
if meos_sys::stbox_tmin(self.inner(), ptr) {
Some(from_meos_timestamp(value))
} else {
None
}
}
}
fn tmax(&self) -> Option<DateTime<Utc>> {
unsafe {
let mut value: i64 = 0;
let ptr: *mut i64 = ptr::addr_of_mut!(value);
if meos_sys::stbox_tmax(self.inner(), ptr) {
DateTime::from_timestamp_micros(value)
} else {
None
}
}
}
fn is_tmin_inclusive(&self) -> Option<bool> {
unsafe {
let mut is_inclusive = false;
let ptr: *mut bool = ptr::addr_of_mut!(is_inclusive);
if meos_sys::stbox_tmin_inc(self.inner(), ptr) {
Some(is_inclusive)
} else {
None
}
}
}
fn is_tmax_inclusive(&self) -> Option<bool> {
unsafe {
let mut is_inclusive = false;
let ptr: *mut bool = ptr::addr_of_mut!(is_inclusive);
if meos_sys::stbox_tmax_inc(self.inner(), ptr) {
Some(is_inclusive)
} else {
None
}
}
}
fn expand_time(&self, duration: TimeDelta) -> STBox {
let interval = create_interval(duration);
unsafe {
Self::from_inner(meos_sys::stbox_expand_time(
self.inner(),
std::ptr::addr_of!(interval),
))
}
}
fn shift_scale_time(&self, delta: Option<TimeDelta>, width: Option<TimeDelta>) -> STBox {
let d = {
if let Some(d) = delta {
&raw const *Box::new(create_interval(d))
} else {
std::ptr::null()
}
};
let w = {
if let Some(w) = width {
&raw const *Box::new(create_interval(w))
} else {
std::ptr::null()
}
};
let modified = unsafe { meos_sys::stbox_shift_scale_time(self.inner(), d, w) };
STBox::from_inner(modified)
}
fn round(&self, max_decimals: i32) -> STBox {
let result = unsafe { meos_sys::stbox_round(self.inner(), max_decimals) };
STBox::from_inner(result)
}
fn union(&self, other: &STBox, strict: bool) -> Option<STBox> {
let result = unsafe { meos_sys::union_stbox_stbox(self.inner(), other.inner(), strict) };
if result.is_null() {
None
} else {
Some(STBox::from_inner(result))
}
}
fn intersection(&self, other: &STBox) -> Option<STBox> {
let result = unsafe { meos_sys::intersection_stbox_stbox(self.inner(), other.inner()) };
if result.is_null() {
None
} else {
Some(STBox::from_inner(result))
}
}
fn nearest_approach_distance(&self, other: &STBox) -> f64 {
unsafe { meos_sys::nad_stbox_stbox(self.inner(), other.inner()) }
}
}
impl STBox {
pub fn inner(&self) -> *const meos_sys::STBox {
self._inner.as_ptr()
}
pub fn from_inner(inner: *mut meos_sys::STBox) -> Self {
Self {
_inner: ptr::NonNull::new(inner).expect("Null pointers not allowed"),
}
}
#[cfg(feature = "geos")]
pub fn from_geos(value: &Geometry) -> Self {
let v: Vec<u8> = value.to_wkb().unwrap();
Self::from_wkb(&v)
}
#[cfg(feature = "geos")]
pub fn geos_geometry(&self) -> Option<Geometry> {
Geometry::new_from_wkb(self.as_wkb(WKBVariant::none())).ok()
}
pub fn expand_space(&self, value: f64) -> STBox {
unsafe { Self::from_inner(meos_sys::stbox_expand_space(self.inner(), value)) }
}
}
impl Collection for STBox {
impl_collection!(stbox, STBox);
fn contains(&self, content: &Self::Type) -> bool {
unsafe { meos_sys::contains_stbox_stbox(self.inner(), content.inner()) }
}
}
impl cmp::PartialEq for STBox {
fn eq(&self, other: &Self) -> bool {
unsafe { meos_sys::stbox_eq(self.inner(), other.inner()) }
}
}
impl cmp::Eq for STBox {}
impl Debug for STBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let out_str = unsafe { meos_sys::stbox_out(self.inner(), 3) };
let c_str = unsafe { CStr::from_ptr(out_str) };
let str = c_str.to_str().map_err(|_| std::fmt::Error)?;
let result = f.write_str(str);
unsafe { libc::free(out_str.cast::<c_void>()) };
result
}
}
impl Clone for STBox {
fn clone(&self) -> Self {
unsafe { Self::from_inner(meos_sys::stbox_copy(self.inner())) }
}
}
impl std::str::FromStr for STBox {
type Err = ParseError;
fn from_str(string: &str) -> Result<Self, Self::Err> {
CString::new(string).map_err(|_| ParseError).map(|string| {
let inner = unsafe { meos_sys::stbox_in(string.as_ptr()) };
Self::from_inner(inner)
})
}
}
impl From<&STBox> for TsTzSpan {
fn from(stbox: &STBox) -> Self {
unsafe { TsTzSpan::from_inner(meos_sys::stbox_to_tstzspan(stbox.inner())) }
}
}