Skip to content

Commit

Permalink
impl PartialEq for Attribute, TODO: needs filter list to also have Pa…
Browse files Browse the repository at this point in the history
…rtialEq
  • Loading branch information
rroelke committed Mar 22, 2024
1 parent aeec3a6 commit 49bdc8b
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 6 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tiledb/api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ name = "tiledb"
path = "src/lib.rs"

[dependencies]
serde = "1.0.136"
serde_json = "1.0.114"
tiledb-sys = { workspace = true }

Expand Down
217 changes: 211 additions & 6 deletions tiledb/api/src/array/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ use serde_json::json;
pub use tiledb_sys::Datatype;

use crate::context::Context;
use crate::convert::CAPIConverter;
use crate::convert::{BitsEq, CAPIConverter};
use crate::error::Error;
use crate::filter_list::{FilterList, RawFilterList};
use crate::fn_typed;
use crate::Result as TileDBResult;

pub(crate) enum RawAttribute {
Expand Down Expand Up @@ -213,23 +214,110 @@ impl<'ctx> Attribute<'ctx> {
impl<'ctx> Debug for Attribute<'ctx> {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
let json = json!({
"name": match self.name() {
Ok(name) => name,
Err(e) => format!("<error reading name: {}>", e),
},
"name": self.name(),
"datatype": match self.datatype() {
Ok(dt) => dt
.to_string()
.unwrap_or(String::from("<unrecognized datatype>")),
Err(e) => format!("<error reading datatype: {}>", e),
},
/* TODO: other fields */
"nullable": self.is_nullable(),
"cell_val_num": self.cell_val_num(),
"fill": if self.is_nullable() {
Some(if let Ok(dt) = self.datatype() {
fn_typed!(dt, DT, match self.fill_value_nullable::<DT>() {
Ok((value, nullable)) => json!({
"value": value.to_string(),
"nullable": nullable
}),
Err(e) => serde_json::value::Value::String(format!("<error reading fill value: {}>", e))
})
} else {
serde_json::value::Value::String(String::from("<Could not resolve datatype>"))
})
} else {
None
},
/*
TODO
"filters": match self.filter_list() {
Ok(fl) => format!("{:?}", fl),
Err(e) => format!("<error reading filters: {}>", e)
},
*/
"raw": format!("{:p}", *self.raw)
});
write!(f, "{}", json)
}
}

impl<'c1, 'c2> PartialEq<Attribute<'c2>> for Attribute<'c1> {
fn eq(&self, other: &Attribute<'c2>) -> bool {
let names_match = match (self.name(), other.name()) {
(Ok(mine), Ok(theirs)) => mine == theirs,
_ => false,
};
if !names_match {
return false;
}

let types_match = match (self.datatype(), other.datatype()) {
(Ok(mine), Ok(theirs)) => mine == theirs,
_ => false,
};
if !types_match {
return false;
}

let nullable_match = self.is_nullable() == other.is_nullable();
if !nullable_match {
return false;
}

/*
let filter_match = match (self.filter_list(), other.filter_list()) {
(Ok(mine), Ok(theirs)) => unimplemented!(),
_ => false,
};
if !filter_match {
return false;
}
*/

let cell_val_match = match (self.cell_val_num(), other.cell_val_num()) {
(Ok(mine), Ok(theirs)) => mine == theirs,
_ => false,
};
if !cell_val_match {
return false;
}

let fill_value_match = if self.is_nullable() {
fn_typed!(self.datatype().unwrap(), DT, {
match (
self.fill_value_nullable::<DT>(),
other.fill_value_nullable::<DT>(),
) {
(Ok(mine), Ok(theirs)) => mine.bits_eq(&theirs),
_ => false,
}
})
} else {
fn_typed!(self.datatype().unwrap(), DT, {
match (self.fill_value::<DT>(), other.fill_value::<DT>()) {
(Ok(mine), Ok(theirs)) => mine.bits_eq(&theirs),
_ => false,
}
})
};
if !fill_value_match {
return false;
}

true
}
}

pub struct Builder<'ctx> {
attr: Attribute<'ctx>,
}
Expand Down Expand Up @@ -646,4 +734,121 @@ mod test {

Ok(())
}

#[test]
fn attribute_eq() {
let ctx = Context::new().unwrap();

let start_attr =
|name: &str, dt: Datatype, nullable: bool| -> Builder {
Builder::new(&ctx, name, dt)
.unwrap()
.nullability(nullable)
.unwrap()
};

let default_name = "foo";
let default_dt = Datatype::Int32;
let default_nullable = false;

let default_attr =
|| start_attr(default_name, default_dt, default_nullable);

let base = default_attr().build();

// reflexive
{
let other = default_attr().build();
assert_eq!(base, other);
}

// change name
{
let other = start_attr("bar", default_dt, default_nullable).build();
assert_ne!(base, other);
}

// change type
{
let other =
start_attr(default_name, Datatype::Float64, default_nullable)
.build();
assert_ne!(base, other);
}

// change nullable
{
let other =
start_attr(default_name, default_dt, !default_nullable).build();
assert_ne!(base, other);
}

// change cellval
{
let other = start_attr(default_name, default_dt, default_nullable)
.cell_val_num((base.cell_val_num().unwrap() + 1) * 2)
.expect("Error setting cell val num")
.build();
assert_ne!(base, other);
}

// change fill val when the attribute is not nullable
{
let other = default_attr()
.fill_value(3i32)
.expect("Error setting fill value")
.build();

assert_ne!(base, other);
}

// change fill val when the attribute *is* nullable
{
let default_attr = || default_attr().nullability(false).unwrap();
let base = default_attr().build();

let other = default_attr()
.fill_value(3i32)
.expect("Error setting fill value")
.build();

assert_ne!(base, other);
}

// change fill nullable
{
let default_attr = || default_attr().nullability(true).unwrap();
let base = default_attr().build();

let (base_fill_value, base_fill_nullable) =
base.fill_value_nullable::<i32>().unwrap();
{
let other = default_attr()
.fill_value_nullability(base_fill_value, base_fill_nullable)
.expect("Error setting fill value")
.build();
assert_eq!(base, other);
}
{
let other = default_attr()
.fill_value_nullability(
base_fill_value,
!base_fill_nullable,
)
.expect("Error setting fill value")
.build();
assert_ne!(base, other);
}
{
let other = default_attr()
.fill_value_nullability(
(base_fill_value / 2) + 1,
base_fill_nullable,
)
.expect("Error setting fill value")
.build();
assert_ne!(base, other);
}
}
}
}
11 changes: 11 additions & 0 deletions tiledb/api/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use std::convert::Into;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::str::FromStr;

use serde::{Serialize, Serializer};

pub(crate) enum ErrorData {
Native(*mut ffi::tiledb_error_t),
Custom(String),
Expand Down Expand Up @@ -98,4 +100,13 @@ impl Drop for Error {
}
}

impl Serialize for Error {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
format!("<{}>", self.get_message()).serialize(serializer)
}
}

impl std::error::Error for Error {}
1 change: 1 addition & 0 deletions tiledb/api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
extern crate serde;
extern crate serde_json;
extern crate tiledb_sys as ffi;

Expand Down
9 changes: 9 additions & 0 deletions tiledb/test/src/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,13 @@ mod tests {

proptest!(|(_ in arbitrary(&ctx))| {});
}

#[test]
fn attribute_eq_reflexivity() {
let ctx = Context::new().expect("Error creating context");

proptest!(|(attr in arbitrary(&ctx))| {
assert_eq!(attr, attr);
});
}
}

0 comments on commit 49bdc8b

Please sign in to comment.