use std::{collections::BTreeSet, fmt::Display};
use crate::{
error::Error,
types::{heading::Heading, tuple::Tuple},
};
#[derive(Debug, Default)]
pub struct RelationBuilder {
heading: Option<Heading>,
body: Vec<Tuple>,
}
impl RelationBuilder {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_heading(mut self, heading: Heading) -> Self {
self.heading = Some(heading);
self
}
#[must_use]
pub fn with_body<T>(mut self, body: T) -> Self
where
T: IntoIterator<Item = Tuple>,
{
self.body = body.into_iter().collect();
self
}
pub fn build(self) -> Result<Relation, Error> {
let heading = self.heading.ok_or(Error::HeadingMissing)?;
let mut relation = Relation::new(heading);
for tuple in self.body {
relation.insert(tuple)?;
}
Ok(relation)
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct Relation {
pub(crate) heading: Heading,
pub(crate) body: BTreeSet<Tuple>,
}
impl Relation {
#[must_use]
pub fn new(heading: Heading) -> Self {
Self {
heading,
body: BTreeSet::new(),
}
}
pub fn new_from_iter<T>(heading: Heading, body: T) -> Result<Self, Error>
where
T: IntoIterator<Item = Tuple>,
{
let mut relation = Self::new(heading);
for item in body {
relation.insert(item)?;
}
Ok(relation)
}
pub fn insert(&mut self, tuple: Tuple) -> Result<(), Error> {
self.heading.validate_tuple(&tuple)?;
self.body.insert(tuple);
Ok(())
}
pub fn iter(&self) -> impl Iterator<Item = &Tuple> {
self.body.iter()
}
}
impl Display for Relation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "RELATION ")?;
write!(f, "{}", self.heading)?;
for tuple in &self.body {
write!(f, "\n\t{tuple}")?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::prelude::{AttributeName, Scalar, ScalarType};
use super::*;
#[test]
fn test_insert() {
let mut relation = Relation::new(
Heading::try_from(vec![
("a".to_string(), ScalarType::Integer),
("b".to_string(), ScalarType::Integer),
])
.unwrap(),
);
let tuple =
Tuple::try_from(vec![("a", Scalar::Integer(1)), ("b", Scalar::Integer(2))]).unwrap();
assert!(relation.insert(tuple).is_ok());
}
#[test]
fn test_insert_invalid_tuple() {
let mut relation = Relation::new(
Heading::try_from(vec![
("a".to_string(), ScalarType::Integer),
("b".to_string(), ScalarType::Integer),
])
.unwrap(),
);
let tuple = Tuple::try_from(vec![
("a".to_string(), Scalar::Integer(1)),
("b".to_string(), Scalar::Boolean(true)),
])
.unwrap();
assert!(relation.insert(tuple).is_err());
}
#[test]
fn test_new_creates_empty_body() {
let relation = Relation::new(
Heading::try_from(vec![(AttributeName::from("a"), ScalarType::Integer)]).unwrap(),
);
assert!(relation.body.is_empty());
}
#[test]
fn test_new_from_iter_rejects_invalid_tuple() {
assert_eq!(
Relation::new_from_iter(
Heading::try_from(vec![(AttributeName::from("a"), ScalarType::Integer)]).unwrap(),
vec![
Tuple::try_from(vec![(AttributeName::from("a"), Scalar::Boolean(true))])
.unwrap(),
],
),
Err(Error::ScalarTypeMismatch {
lhs: ScalarType::Boolean,
rhs: ScalarType::Integer
})
);
}
#[test]
fn test_new_from_iter_deduplicates_duplicate_tuples() -> Result<(), Error> {
let relation = Relation::new_from_iter(
Heading::try_from(vec![(AttributeName::from("a"), ScalarType::Integer)]).unwrap(),
vec![
Tuple::try_from(vec![(AttributeName::from("a"), Scalar::Integer(1))]).unwrap(),
Tuple::try_from(vec![(AttributeName::from("a"), Scalar::Integer(1))]).unwrap(),
],
)?;
assert_eq!(relation.body.len(), 1);
Ok(())
}
#[test]
fn test_iter_returns_tuples_in_sorted_order() -> Result<(), Error> {
let relation = Relation::new_from_iter(
Heading::try_from(vec![(AttributeName::from("a"), ScalarType::Integer)]).unwrap(),
vec![
Tuple::try_from(vec![(AttributeName::from("a"), Scalar::Integer(2))]).unwrap(),
Tuple::try_from(vec![(AttributeName::from("a"), Scalar::Integer(1))]).unwrap(),
],
)?;
let tuples = relation.iter().cloned().collect::<Vec<_>>();
assert_eq!(
tuples,
vec![
Tuple::try_from(vec![(AttributeName::from("a"), Scalar::Integer(1))]).unwrap(),
Tuple::try_from(vec![(AttributeName::from("a"), Scalar::Integer(2))]).unwrap(),
]
);
Ok(())
}
}