querydown 0.0.1

An expressive DSL for writing relational database queries that compile to SQL
Documentation
use std::ops::{BitAnd, Not};

use crate::syntax_tree::ConditionSet;

use super::schema::{ColumnId, Schema, TableId};
use JoinQuantity::*;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ForwardLinkToOne {
    pub base: Reference,
    pub target: Reference,
}

impl From<ForeignKey> for ForwardLinkToOne {
    fn from(foreign_key: ForeignKey) -> Self {
        Self {
            base: foreign_key.base,
            target: foreign_key.target,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ReverseLinkToOne {
    pub base: Reference,
    pub target: Reference,
}

impl From<ForeignKey> for ReverseLinkToOne {
    fn from(foreign_key: ForeignKey) -> Self {
        Self {
            base: foreign_key.base,
            target: foreign_key.target,
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct ReverseLinkToMany {
    pub base: Reference,
    pub target: Reference,
}

impl From<ForeignKey> for ReverseLinkToMany {
    fn from(foreign_key: ForeignKey) -> Self {
        Self {
            base: foreign_key.base,
            target: foreign_key.target,
        }
    }
}

pub trait Link {
    fn get_start(&self) -> Reference;
    fn get_end(&self) -> Reference;
    fn get_base(&self) -> Reference;
    fn get_target(&self) -> Reference;
    fn get_direction(&self) -> LinkDirection;
    fn get_join_quantity(&self) -> JoinQuantity;
}

impl Link for ForwardLinkToOne {
    fn get_direction(&self) -> LinkDirection {
        LinkDirection::Forward
    }

    fn get_join_quantity(&self) -> JoinQuantity {
        One
    }

    fn get_start(&self) -> Reference {
        self.base
    }

    fn get_end(&self) -> Reference {
        self.target
    }

    fn get_base(&self) -> Reference {
        self.base
    }

    fn get_target(&self) -> Reference {
        self.target
    }
}

impl Link for ReverseLinkToOne {
    fn get_direction(&self) -> LinkDirection {
        LinkDirection::Reverse
    }

    fn get_join_quantity(&self) -> JoinQuantity {
        One
    }

    fn get_start(&self) -> Reference {
        self.target
    }

    fn get_end(&self) -> Reference {
        self.base
    }

    fn get_base(&self) -> Reference {
        self.base
    }

    fn get_target(&self) -> Reference {
        self.target
    }
}

impl Link for ReverseLinkToMany {
    fn get_direction(&self) -> LinkDirection {
        LinkDirection::Reverse
    }

    fn get_join_quantity(&self) -> JoinQuantity {
        Many
    }

    fn get_start(&self) -> Reference {
        self.target
    }

    fn get_end(&self) -> Reference {
        self.base
    }

    fn get_base(&self) -> Reference {
        self.base
    }

    fn get_target(&self) -> Reference {
        self.target
    }
}

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum JoinQuantity {
    One,
    Many,
}

impl BitAnd for JoinQuantity {
    type Output = Self;

    fn bitand(self, rhs: Self) -> Self::Output {
        match (self, rhs) {
            (One, One) => One,
            (One, Many) => Many,
            (Many, One) => Many,
            (Many, Many) => Many,
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub enum LinkDirection {
    Forward,
    Reverse,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Reference {
    pub table_id: TableId,
    pub column_id: ColumnId,
}

impl Reference {
    pub fn new(table_id: TableId, column_id: ColumnId) -> Self {
        Self {
            table_id,
            column_id,
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct ForeignKey {
    pub base: Reference,
    pub target: Reference,
    pub unique: bool,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LinkToOne {
    ForwardLinkToOne(ForwardLinkToOne),
    ReverseLinkToOne(ReverseLinkToOne),
}

impl Link for LinkToOne {
    fn get_direction(&self) -> LinkDirection {
        match self {
            LinkToOne::ForwardLinkToOne(link) => link.get_direction(),
            LinkToOne::ReverseLinkToOne(link) => link.get_direction(),
        }
    }

    fn get_join_quantity(&self) -> JoinQuantity {
        match self {
            LinkToOne::ForwardLinkToOne(link) => link.get_join_quantity(),
            LinkToOne::ReverseLinkToOne(link) => link.get_join_quantity(),
        }
    }

    fn get_start(&self) -> Reference {
        match self {
            LinkToOne::ForwardLinkToOne(link) => link.get_start(),
            LinkToOne::ReverseLinkToOne(link) => link.get_start(),
        }
    }

    fn get_end(&self) -> Reference {
        match self {
            LinkToOne::ForwardLinkToOne(link) => link.get_end(),
            LinkToOne::ReverseLinkToOne(link) => link.get_end(),
        }
    }

    fn get_base(&self) -> Reference {
        match self {
            LinkToOne::ForwardLinkToOne(link) => link.get_base(),
            LinkToOne::ReverseLinkToOne(link) => link.get_base(),
        }
    }

    fn get_target(&self) -> Reference {
        match self {
            LinkToOne::ForwardLinkToOne(link) => link.get_target(),
            LinkToOne::ReverseLinkToOne(link) => link.get_target(),
        }
    }
}

impl TryFrom<FilteredLink> for LinkToOne {
    type Error = FilteredLink;

    fn try_from(filtered_link: FilteredLink) -> Result<Self, Self::Error> {
        if filtered_link.condition_set.is_empty().not() {
            return Err(filtered_link);
        }
        match filtered_link.link {
            MultiLink::ForwardLinkToOne(link) => Ok(LinkToOne::ForwardLinkToOne(link)),
            MultiLink::ReverseLinkToOne(link) => Ok(LinkToOne::ReverseLinkToOne(link)),
            _ => Err(filtered_link),
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub enum MultiLink {
    ForwardLinkToOne(ForwardLinkToOne),
    ReverseLinkToOne(ReverseLinkToOne),
    ReverseLinkToMany(ReverseLinkToMany),
}

impl Link for MultiLink {
    fn get_direction(&self) -> LinkDirection {
        match self {
            MultiLink::ForwardLinkToOne(link) => link.get_direction(),
            MultiLink::ReverseLinkToOne(link) => link.get_direction(),
            MultiLink::ReverseLinkToMany(link) => link.get_direction(),
        }
    }

    fn get_join_quantity(&self) -> JoinQuantity {
        match self {
            MultiLink::ForwardLinkToOne(link) => link.get_join_quantity(),
            MultiLink::ReverseLinkToOne(link) => link.get_join_quantity(),
            MultiLink::ReverseLinkToMany(link) => link.get_join_quantity(),
        }
    }

    fn get_start(&self) -> Reference {
        match self {
            MultiLink::ForwardLinkToOne(link) => link.get_start(),
            MultiLink::ReverseLinkToOne(link) => link.get_start(),
            MultiLink::ReverseLinkToMany(link) => link.get_start(),
        }
    }

    fn get_end(&self) -> Reference {
        match self {
            MultiLink::ForwardLinkToOne(link) => link.get_end(),
            MultiLink::ReverseLinkToOne(link) => link.get_end(),
            MultiLink::ReverseLinkToMany(link) => link.get_end(),
        }
    }

    fn get_base(&self) -> Reference {
        match self {
            MultiLink::ForwardLinkToOne(link) => link.get_base(),
            MultiLink::ReverseLinkToOne(link) => link.get_base(),
            MultiLink::ReverseLinkToMany(link) => link.get_base(),
        }
    }

    fn get_target(&self) -> Reference {
        match self {
            MultiLink::ForwardLinkToOne(link) => link.get_target(),
            MultiLink::ReverseLinkToOne(link) => link.get_target(),
            MultiLink::ReverseLinkToMany(link) => link.get_target(),
        }
    }
}

#[derive(Debug)]
pub struct FilteredLink {
    pub link: MultiLink,
    pub condition_set: ConditionSet,
}

impl From<MultiLink> for FilteredLink {
    fn from(link: MultiLink) -> Self {
        Self {
            link,
            condition_set: ConditionSet::default(),
        }
    }
}

impl Link for FilteredLink {
    fn get_direction(&self) -> LinkDirection {
        self.link.get_direction()
    }

    fn get_join_quantity(&self) -> JoinQuantity {
        self.link.get_join_quantity()
    }

    fn get_start(&self) -> Reference {
        self.link.get_start()
    }

    fn get_end(&self) -> Reference {
        self.link.get_end()
    }

    fn get_base(&self) -> Reference {
        self.link.get_base()
    }

    fn get_target(&self) -> Reference {
        self.link.get_target()
    }
}