diff --git a/examples/quickstart_dense.rs b/examples/quickstart_dense.rs index dc16c3eb..94784f11 100644 --- a/examples/quickstart_dense.rs +++ b/examples/quickstart_dense.rs @@ -1,5 +1,6 @@ extern crate tiledb; +use tiledb::Datatype; use tiledb::Result as TileDBResult; const QUICKSTART_DENSE_ARRAY_URI: &str = "quickstart_dense_array"; @@ -25,6 +26,7 @@ fn create_array() -> TileDBResult<()> { tiledb::array::DimensionBuilder::new::( &tdb, "rows", + Datatype::Int32, &[1, 4], &4, )? @@ -33,6 +35,7 @@ fn create_array() -> TileDBResult<()> { tiledb::array::DimensionBuilder::new::( &tdb, "columns", + Datatype::Int32, &[1, 4], &4, )? diff --git a/src/array/attribute.rs b/src/array/attribute.rs index 5ecf2796..a5cd742b 100644 --- a/src/array/attribute.rs +++ b/src/array/attribute.rs @@ -5,7 +5,7 @@ use std::ops::Deref; pub use tiledb_sys::Datatype; use crate::context::Context; -use crate::datatype::DomainType; +use crate::convert::CAPIConverter; use crate::error::Error; use crate::filter_list::FilterList; use crate::Result as TileDBResult; @@ -237,18 +237,18 @@ impl Attribute { } // This currently does not support setting multi-value cells. - pub fn set_fill_value( + pub fn set_fill_value( &self, ctx: &Context, - value: DT, + value: Conv, ) -> TileDBResult<()> { - let c_val: DT::CApiType = value.as_capi(); + let c_val: Conv::CAPIType = value.to_capi(); - if DT::DATATYPE != self.datatype(ctx)? { + if !self.datatype(ctx)?.is_valid(&value) { return Err(Error::from(format!( "Dimension type mismatch: expected {}, found {}", self.datatype(ctx)?, - DT::DATATYPE + std::any::type_name::() ))); } @@ -256,8 +256,8 @@ impl Attribute { ffi::tiledb_attribute_set_fill_value( ctx.as_mut_ptr(), *self.raw, - &c_val as *const DT::CApiType as *const std::ffi::c_void, - std::mem::size_of::() as u64, + &c_val as *const Conv::CAPIType as *const std::ffi::c_void, + std::mem::size_of::() as u64, ) }; @@ -268,10 +268,10 @@ impl Attribute { } } - pub fn get_fill_value( + pub fn get_fill_value( &self, ctx: &Context, - ) -> TileDBResult
{ + ) -> TileDBResult { let mut c_ptr: *const std::ffi::c_void = out_ptr!(); let mut c_size: u64 = 0; @@ -288,13 +288,13 @@ impl Attribute { return Err(ctx.expect_last_error()); } - if c_size != std::mem::size_of::() as u64 { + if c_size != std::mem::size_of::() as u64 { return Err(Error::from("Invalid value size returned by TileDB")); } - let c_val: DT::CApiType = unsafe { *c_ptr.cast::() }; + let c_val: Conv::CAPIType = unsafe { *c_ptr.cast::() }; - Ok(DT::from_capi(&c_val)) + Ok(Conv::to_rust(&c_val)) } } diff --git a/src/array/dimension.rs b/src/array/dimension.rs index ac156137..70e3e88e 100644 --- a/src/array/dimension.rs +++ b/src/array/dimension.rs @@ -1,8 +1,9 @@ use std::ops::Deref; +use tiledb_sys::Datatype; + use crate::context::Context; -use crate::datatype::{Datatype, DomainType}; -use crate::error::Error; +use crate::convert::CAPIConverter; use crate::filter_list::FilterList; use crate::Result as TileDBResult; @@ -56,19 +57,11 @@ impl<'ctx> Dimension<'ctx> { Datatype::from_capi_enum(c_datatype) } - pub fn domain(&self) -> TileDBResult<[DT; 2]> { + pub fn domain(&self) -> TileDBResult<[Conv; 2]> { let c_context = self.context.as_mut_ptr(); let c_dimension = self.capi(); let mut c_domain_ptr: *const std::ffi::c_void = out_ptr!(); - if DT::DATATYPE != self.datatype() { - return Err(Error::from(format!( - "Dimension type mismatch: expected {}, found {}", - self.datatype(), - DT::DATATYPE - ))); - } - let c_ret = unsafe { ffi::tiledb_dimension_get_domain( c_context, @@ -80,10 +73,10 @@ impl<'ctx> Dimension<'ctx> { // the only errors are possible via mis-use of the C API, which Rust prevents assert_eq!(ffi::TILEDB_OK, c_ret); - let c_domain: &[DT::CApiType; 2] = - unsafe { &*c_domain_ptr.cast::<[DT::CApiType; 2]>() }; + let c_domain: &[Conv::CAPIType; 2] = + unsafe { &*c_domain_ptr.cast::<[Conv::CAPIType; 2]>() }; - Ok([DT::from_capi(&c_domain[0]), DT::from_capi(&c_domain[1])]) + Ok([Conv::to_rust(&c_domain[0]), Conv::to_rust(&c_domain[1])]) } pub fn filters(&self) -> FilterList { @@ -113,20 +106,21 @@ pub struct Builder<'ctx> { impl<'ctx> Builder<'ctx> { // TODO: extent might be optional? // and it - pub fn new( + pub fn new( context: &'ctx Context, name: &str, - domain: &[DT; 2], - extent: &DT, + datatype: Datatype, + domain: &[Conv; 2], + extent: &Conv, ) -> TileDBResult { let c_context = context.as_mut_ptr(); - let c_datatype = DT::DATATYPE.capi_enum(); + let c_datatype = datatype.capi_enum(); let c_name = cstring!(name); - let c_domain: [DT::CApiType; 2] = - [domain[0].as_capi(), domain[1].as_capi()]; - let c_extent: DT::CApiType = extent.as_capi(); + let c_domain: [Conv::CAPIType; 2] = + [domain[0].to_capi(), domain[1].to_capi()]; + let c_extent: Conv::CAPIType = extent.to_capi(); let mut c_dimension: *mut ffi::tiledb_dimension_t = std::ptr::null_mut(); @@ -136,10 +130,9 @@ impl<'ctx> Builder<'ctx> { c_context, c_name.as_ptr(), c_datatype, - &c_domain[0] as *const
::CApiType - as *const std::ffi::c_void, - &c_extent as *const
::CApiType + &c_domain[0] as *const ::CAPIType as *const std::ffi::c_void, + &c_extent as *const ::CAPIType as *const std::ffi::c_void, &mut c_dimension, ) } == ffi::TILEDB_OK @@ -197,6 +190,7 @@ mod tests { Builder::new::( &context, "test_dimension_alloc", + Datatype::Int32, &domain, &extent, ) @@ -210,6 +204,7 @@ mod tests { let b = Builder::new::( &context, "test_dimension_alloc", + Datatype::Int32, &domain, &extent, ); @@ -223,6 +218,7 @@ mod tests { let b = Builder::new::( &context, "test_dimension_alloc", + Datatype::Int32, &domain, &extent, ); @@ -241,6 +237,7 @@ mod tests { let dim = Builder::new::( &context, "test_dimension_domain", + Datatype::Int32, &domain_in, &extent, ) @@ -266,6 +263,7 @@ mod tests { let dimension: Dimension = Builder::new::( &context, "test_dimension_alloc", + Datatype::Int32, &domain, &extent, ) @@ -286,6 +284,7 @@ mod tests { let dimension: Dimension = Builder::new::( &context, "test_dimension_alloc", + Datatype::Int32, &domain, &extent, ) diff --git a/src/array/domain.rs b/src/array/domain.rs index 5caf7370..9432e41e 100644 --- a/src/array/domain.rs +++ b/src/array/domain.rs @@ -150,6 +150,7 @@ mod tests { DimensionBuilder::new::( &context, "d1", + Datatype::Int32, &dim_domain, &extent, ) @@ -179,6 +180,7 @@ mod tests { DimensionBuilder::new::( &context, "d1", + Datatype::Int32, &dim1_domain, &extent, ) @@ -191,6 +193,7 @@ mod tests { DimensionBuilder::new::( &context, "d2", + Datatype::Float64, &dim2_domain, &extent, ) diff --git a/src/array/mod.rs b/src/array/mod.rs index 8667c34e..675e31c1 100644 --- a/src/array/mod.rs +++ b/src/array/mod.rs @@ -165,14 +165,24 @@ mod tests { // "quickstart_dense" example let d: Domain = { - let rows: Dimension = - DimensionBuilder::new::(&c, "rows", &[1, 4], &4) - .expect("Error constructing rows dimension") - .build(); - let cols: Dimension = - DimensionBuilder::new::(&c, "cols", &[1, 4], &4) - .expect("Error constructing cols dimension") - .build(); + let rows: Dimension = DimensionBuilder::new::( + &c, + "rows", + Datatype::Int32, + &[1, 4], + &4, + ) + .expect("Error constructing rows dimension") + .build(); + let cols: Dimension = DimensionBuilder::new::( + &c, + "cols", + Datatype::Int32, + &[1, 4], + &4, + ) + .expect("Error constructing cols dimension") + .build(); DomainBuilder::new(&c) .unwrap() diff --git a/src/array/schema.rs b/src/array/schema.rs index 67bad4c9..f2b03456 100644 --- a/src/array/schema.rs +++ b/src/array/schema.rs @@ -177,12 +177,19 @@ mod tests { use crate::array::schema::*; use crate::array::{DimensionBuilder, DomainBuilder}; use crate::context::Context; + use tiledb_sys::Datatype; /// Helper function to make a Domain which isn't needed for the purposes of the test fn unused_domain(c: &Context) -> Domain { - let dim = DimensionBuilder::new::(c, "test", &[-100, 100], &100) - .unwrap() - .build(); + let dim = DimensionBuilder::new::( + c, + "test", + Datatype::Int32, + &[-100, 100], + &100, + ) + .unwrap() + .build(); DomainBuilder::new(c) .unwrap() .add_dimension(dim) diff --git a/src/convert.rs b/src/convert.rs new file mode 100644 index 00000000..3fa4992a --- /dev/null +++ b/src/convert.rs @@ -0,0 +1,42 @@ +pub trait CAPIConverter { + type CAPIType: Default + Copy; + + fn to_capi(&self) -> Self::CAPIType; + fn to_rust(value: &Self::CAPIType) -> Self; +} + +impl CAPIConverter for i32 { + type CAPIType = std::ffi::c_int; + + fn to_capi(&self) -> Self::CAPIType { + *self as Self::CAPIType + } + + fn to_rust(value: &Self::CAPIType) -> Self { + *value as Self + } +} + +impl CAPIConverter for u32 { + type CAPIType = std::ffi::c_uint; + + fn to_capi(&self) -> Self::CAPIType { + *self as Self::CAPIType + } + + fn to_rust(value: &Self::CAPIType) -> Self { + *value as Self + } +} + +impl CAPIConverter for f64 { + type CAPIType = std::ffi::c_double; + + fn to_capi(&self) -> Self::CAPIType { + *self as Self::CAPIType + } + + fn to_rust(value: &Self::CAPIType) -> Self { + *value as Self + } +} diff --git a/src/datatype.rs b/src/datatype.rs index 442bb162..aa99a9a8 100644 --- a/src/datatype.rs +++ b/src/datatype.rs @@ -1,52 +1,52 @@ pub use ffi::Datatype; -pub trait DomainType { - const DATATYPE: Datatype; - - type CApiType: Default + Copy; - - fn as_capi(&self) -> Self::CApiType; - fn from_capi(capi: &Self::CApiType) -> Self; -} - -impl DomainType for i32 { - const DATATYPE: Datatype = Datatype::Int32; - - type CApiType = std::ffi::c_int; - - fn as_capi(&self) -> Self::CApiType { - *self as Self::CApiType - } - - fn from_capi(capi: &Self::CApiType) -> Self { - *capi as Self - } -} - -impl DomainType for u32 { - const DATATYPE: Datatype = Datatype::UInt32; - - type CApiType = std::ffi::c_uint; - - fn as_capi(&self) -> Self::CApiType { - *self as Self::CApiType - } - - fn from_capi(capi: &Self::CApiType) -> Self { - *capi as Self - } -} - -impl DomainType for f64 { - const DATATYPE: Datatype = Datatype::Float64; - - type CApiType = std::ffi::c_double; - - fn as_capi(&self) -> Self::CApiType { - *self as Self::CApiType - } - - fn from_capi(capi: &Self::CApiType) -> Self { - *capi as Self - } -} +// pub trait DomainType { +// const DATATYPE: Datatype; +// +// type CApiType: Default + Copy; +// +// fn as_capi(&self) -> Self::CApiType; +// fn from_capi(capi: &Self::CApiType) -> Self; +// } +// +// impl DomainType for i32 { +// const DATATYPE: Datatype = Datatype::Int32; +// +// type CApiType = std::ffi::c_int; +// +// fn as_capi(&self) -> Self::CApiType { +// *self as Self::CApiType +// } +// +// fn from_capi(capi: &Self::CApiType) -> Self { +// *capi as Self +// } +// } +// +// impl DomainType for u32 { +// const DATATYPE: Datatype = Datatype::UInt32; +// +// type CApiType = std::ffi::c_uint; +// +// fn as_capi(&self) -> Self::CApiType { +// *self as Self::CApiType +// } +// +// fn from_capi(capi: &Self::CApiType) -> Self { +// *capi as Self +// } +// } +// +// impl DomainType for f64 { +// const DATATYPE: Datatype = Datatype::Float64; +// +// type CApiType = std::ffi::c_double; +// +// fn as_capi(&self) -> Self::CApiType { +// *self as Self::CApiType +// } +// +// fn from_capi(capi: &Self::CApiType) -> Self { +// *capi as Self +// } +// } diff --git a/src/lib.rs b/src/lib.rs index c9f3a3d4..f74b95e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,7 @@ macro_rules! out_ptr { pub mod array; pub mod config; pub mod context; +pub mod convert; pub mod datatype; pub mod error; pub mod filter; diff --git a/src/query/mod.rs b/src/query/mod.rs index 36c2bd60..545e09c7 100644 --- a/src/query/mod.rs +++ b/src/query/mod.rs @@ -5,7 +5,7 @@ use std::ops::Deref; use crate::array::Layout; use crate::context::Context; -use crate::datatype::DomainType; +use crate::convert::CAPIConverter; use crate::{Array, Result as TileDBResult}; pub use crate::query::subarray::{Builder as SubarrayBuilder, Subarray}; @@ -114,16 +114,16 @@ impl<'ctx> Builder<'ctx> { SubarrayBuilder::for_query(self) } - pub fn dimension_buffer_typed( + pub fn dimension_buffer_typed( mut self, name: &str, - data: &mut [DT], + data: &mut [Conv], ) -> TileDBResult { let c_context = self.query.context.as_mut_ptr(); let c_query = *self.query.raw; let c_name = cstring!(name); - let c_bufptr = &mut data[0] as *mut DT as *mut std::ffi::c_void; + let c_bufptr = &mut data[0] as *mut Conv as *mut std::ffi::c_void; let mut c_size = Box::new((std::mem::size_of_val(data)).try_into().unwrap()); diff --git a/src/query/subarray.rs b/src/query/subarray.rs index ce9de6b5..0a5db833 100644 --- a/src/query/subarray.rs +++ b/src/query/subarray.rs @@ -1,7 +1,7 @@ use std::ops::Deref; use crate::context::Context; -use crate::datatype::DomainType; +use crate::convert::CAPIConverter; use crate::query::Builder as QueryBuilder; use crate::Result as TileDBResult; @@ -57,16 +57,16 @@ impl<'ctx> Builder<'ctx> { } } - pub fn dimension_range_typed( + pub fn dimension_range_typed( self, idx: u32, - range: &[DT; 2], + range: &[Conv; 2], ) -> TileDBResult> { let c_context = self.subarray.context.as_mut_ptr(); let c_subarray = *self.subarray.raw; - let c_start = &range[0] as *const DT as *const std::ffi::c_void; - let c_end = &range[1] as *const DT as *const std::ffi::c_void; + let c_start = &range[0] as *const Conv as *const std::ffi::c_void; + let c_end = &range[1] as *const Conv as *const std::ffi::c_void; let c_ret = unsafe { ffi::tiledb_subarray_add_range( diff --git a/tiledb-sys/src/datatype.rs b/tiledb-sys/src/datatype.rs index 349e89d0..cb6eacd7 100644 --- a/tiledb-sys/src/datatype.rs +++ b/tiledb-sys/src/datatype.rs @@ -1,3 +1,4 @@ +use std::any::TypeId; use std::fmt::{Display, Formatter, Result as FmtResult}; use crate::constants::TILEDB_OK; @@ -17,6 +18,7 @@ extern "C" { pub fn tiledb_datatype_size(type_: u32) -> u64; } +#[allow(non_snake_case)] pub type tiledb_datatype_t = ::std::os::raw::c_uint; // When I find the time, I should come back and turn these into a macro @@ -207,6 +209,96 @@ impl Datatype { _ => None, } } + + pub fn is_valid(&self, _: &T) -> bool { + if TypeId::of::() == TypeId::of::() { + match self { + Datatype::Float32 => true, + _ => false, + } + } else if TypeId::of::() == TypeId::of::() { + match self { + Datatype::Float64 => true, + _ => false, + } + } else if TypeId::of::() == TypeId::of::() { + match self { + Datatype::Char => true, + Datatype::Int8 => true, + _ => false, + } + } else if TypeId::of::() == TypeId::of::() { + match self { + Datatype::Any => true, + Datatype::Blob => true, + Datatype::Boolean => true, + Datatype::GeometryWkb => true, + Datatype::GeometryWkt => true, + Datatype::StringAscii => true, + Datatype::StringUtf8 => true, + Datatype::UInt8 => true, + _ => false, + } + } else if TypeId::of::() == TypeId::of::() { + match self { + Datatype::Int16 => true, + _ => false, + } + } else if TypeId::of::() == TypeId::of::() { + match self { + Datatype::StringUtf16 => true, + Datatype::StringUcs2 => true, + Datatype::UInt16 => true, + _ => false, + } + } else if TypeId::of::() == TypeId::of::() { + match self { + Datatype::Int32 => true, + _ => false, + } + } else if TypeId::of::() == TypeId::of::() { + match self { + Datatype::StringUtf32 => true, + Datatype::StringUcs4 => true, + Datatype::UInt32 => true, + _ => false, + } + } else if TypeId::of::() == TypeId::of::() { + match self { + Datatype::Int64 => true, + Datatype::DateTimeYear => true, + Datatype::DateTimeMonth => true, + Datatype::DateTimeWeek => true, + Datatype::DateTimeDay => true, + Datatype::DateTimeHour => true, + Datatype::DateTimeMinute => true, + Datatype::DateTimeSecond => true, + Datatype::DateTimeMillisecond => true, + Datatype::DateTimeMicrosecond => true, + Datatype::DateTimeNanosecond => true, + Datatype::DateTimePicosecond => true, + Datatype::DateTimeFemtosecond => true, + Datatype::DateTimeAttosecond => true, + Datatype::TimeHour => true, + Datatype::TimeMinute => true, + Datatype::TimeSecond => true, + Datatype::TimeMillisecond => true, + Datatype::TimeMicrosecond => true, + Datatype::TimeNanosecond => true, + Datatype::TimePicosecond => true, + Datatype::TimeFemtosecond => true, + Datatype::TimeAttosecond => true, + _ => false, + } + } else if TypeId::of::() == TypeId::of::() { + match self { + Datatype::UInt64 => true, + _ => false, + } + } else { + false + } + } } impl Display for Datatype { @@ -224,10 +316,80 @@ impl Display for Datatype { #[cfg(test)] mod tests { + use super::*; + #[test] fn datatype_test() { for i in 0..256 { - println!("{}", i); + println!("I: {}", i); + if i <= 43 { + let dt = Datatype::from_u32(i as u32) + .expect("Error converting value to Datatype"); + assert_ne!( + format!("{}", dt), + "".to_string() + ); + assert!(check_valid(&dt)); + } else { + assert!(Datatype::from_u32(i as u32).is_none()); + } + } + } + + fn check_valid(dt: &Datatype) -> bool { + let v0: f32 = 0.0; + let v1: f64 = 0.0; + let v2: i8 = 0; + let v3: u8 = 0; + let v4: i16 = 0; + let v5: u16 = 0; + let v6: i32 = 0; + let v7: u32 = 0; + let v8: i64 = 0; + let v9: u64 = 0; + + let mut count = 0; + + if dt.is_valid(&v0) { + count += 1; + } + + if dt.is_valid(&v1) { + count += 1; + } + + if dt.is_valid(&v2) { + count += 1; + } + + if dt.is_valid(&v3) { + count += 1; + } + + if dt.is_valid(&v4) { + count += 1; + } + + if dt.is_valid(&v5) { + count += 1; + } + + if dt.is_valid(&v6) { + count += 1; + } + + if dt.is_valid(&v7) { + count += 1; } + + if dt.is_valid(&v8) { + count += 1; + } + + if dt.is_valid(&v9) { + count += 1; + } + + count == 1 } }