use std::{collections::BTreeMap, fmt::Display};
use crate::{
error::Error,
types::{scalar::Scalar, AttributeName},
};
#[macro_export]
macro_rules! tuple {
($($key:ident = $value:expr),* $(,)?) => {
$crate::TupleBuilder::new()
$(
.with_value(stringify!($key), $value)
)*
.build()
};
}
#[derive(Debug, Default)]
pub struct TupleBuilder {
values: Vec<(AttributeName, Scalar)>,
}
impl TupleBuilder {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_value<A, S>(mut self, attribute: A, value: S) -> Self
where
A: Into<AttributeName>,
S: Into<Scalar>,
{
self.values.push((attribute.into(), value.into()));
self
}
pub fn build(self) -> Result<Tuple, Error> {
Tuple::try_from(self.values)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Tuple {
pub(crate) values: BTreeMap<AttributeName, Scalar>,
}
impl Tuple {
#[must_use]
pub fn empty() -> Self {
Self {
values: BTreeMap::new(),
}
}
#[must_use]
pub fn arity(&self) -> usize {
self.values.len()
}
#[must_use]
pub fn get(&self, attribute: &AttributeName) -> Option<&Scalar> {
self.values.get(attribute)
}
pub fn iter(&self) -> impl Iterator<Item = (&AttributeName, &Scalar)> {
self.values.iter()
}
}
impl<K, V> TryFrom<Vec<(K, V)>> for Tuple
where
K: Into<AttributeName>,
V: Into<Scalar>,
{
type Error = Error;
fn try_from(input: Vec<(K, V)>) -> Result<Self, Self::Error> {
let mut values = BTreeMap::new();
for (attribute, value) in input {
let key = attribute.into();
if values.contains_key(&key) {
return Err(Error::AttributeAlreadyExists { name: key });
}
values.insert(key, value.into());
}
Ok(Self { values })
}
}
impl From<BTreeMap<AttributeName, Scalar>> for Tuple {
fn from(input: BTreeMap<AttributeName, Scalar>) -> Self {
Self { values: input }
}
}
impl Display for Tuple {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "TUPLE {{ ")?;
for (name, value) in &self.values {
write!(f, "{name} {value}, ")?;
}
write!(f, "}}")?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use super::*;
#[test]
fn test_arity() {
let input = vec![(AttributeName::from("foo"), Scalar::Integer(42))];
let tuple = Tuple::try_from(input).unwrap();
assert_eq!(tuple.arity(), 1);
}
#[test]
fn test_get() {
let input = vec![(AttributeName::from("foo"), Scalar::Integer(42))];
let tuple = Tuple::try_from(input).unwrap();
assert_eq!(
tuple.get(&AttributeName::from("foo")).unwrap(),
&Scalar::Integer(42)
);
}
#[test]
fn test_from() {
let input = vec![(AttributeName::from("foo"), Scalar::Integer(42))];
let tuple = Tuple::try_from(input).unwrap();
assert_eq!(tuple.values.len(), 1);
assert_eq!(
tuple.values.get(&AttributeName::from("foo")).unwrap(),
&Scalar::Integer(42)
);
}
#[test]
fn test_tuple_equality() {
let tuple1 = Tuple::try_from(vec![
(AttributeName::from("foo"), Scalar::Integer(42)),
(AttributeName::from("bar"), Scalar::Boolean(true)),
])
.unwrap();
let tuple2 = Tuple::try_from(vec![
(AttributeName::from("bar"), Scalar::Boolean(true)),
(AttributeName::from("foo"), Scalar::Integer(42)),
])
.unwrap();
assert_eq!(tuple1, tuple2);
}
#[test]
fn test_tuple_not_equality() {
let tuple1 =
Tuple::try_from(vec![(AttributeName::from("foo"), Scalar::Integer(42))]).unwrap();
let tuple2 =
Tuple::try_from(vec![(AttributeName::from("foo"), Scalar::Boolean(true))]).unwrap();
assert_ne!(tuple1, tuple2);
}
#[test]
fn test_get_missing_attribute_returns_none() {
let tuple =
Tuple::try_from(vec![(AttributeName::from("foo"), Scalar::Integer(42))]).unwrap();
assert_eq!(tuple.get(&AttributeName::from("bar")), None);
}
#[test]
fn test_from_btreemap() {
let mut values = BTreeMap::new();
values.insert(AttributeName::from("foo"), Scalar::Integer(42));
let tuple = Tuple::try_from(values).unwrap();
assert_eq!(tuple.arity(), 1);
assert_eq!(
tuple.get(&AttributeName::from("foo")),
Some(&Scalar::Integer(42))
);
}
#[test]
fn test_tuple_from_rejects_duplicate_attribute_names() {
assert!(Tuple::try_from(vec![
(AttributeName::from("foo"), Scalar::Integer(1)),
(AttributeName::from("foo"), Scalar::Integer(2)),
])
.is_err());
}
}