rjango 0.1.1

A full-stack Rust backend framework inspired by Django
Documentation
use sea_orm::sea_query::Order;

use super::where_node::WhereNode;

/// Internal query representation (Django's sql.Query equivalent).
#[derive(Debug, Clone, Default)]
pub struct Query {
    pub select: Vec<String>,
    pub where_clause: Option<WhereNode>,
    pub orderings: Vec<(String, Order)>,
    pub group_by: Vec<String>,
    pub having: Option<WhereNode>,
    pub limit: Option<u64>,
    pub offset: Option<u64>,
    pub distinct: bool,
    pub annotations: Vec<Annotation>,
    pub subquery: bool,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Annotation {
    pub alias: String,
    pub expression: String,
}

impl Query {
    #[must_use]
    pub fn new() -> Self {
        Self::default()
    }

    #[must_use]
    pub fn is_empty(&self) -> bool {
        self.select.is_empty() && self.where_clause.is_none()
    }

    #[must_use]
    pub fn has_limit_or_offset(&self) -> bool {
        self.limit.is_some() || self.offset.is_some()
    }

    pub fn clear_ordering(&mut self) {
        self.orderings.clear();
    }

    pub fn clear_limits(&mut self) {
        self.limit = None;
        self.offset = None;
    }

    pub fn add_annotation(&mut self, alias: impl Into<String>, expression: impl Into<String>) {
        self.annotations.push(Annotation {
            alias: alias.into(),
            expression: expression.into(),
        });
    }
}

#[cfg(test)]
mod tests {
    use sea_orm::sea_query::Order;

    use super::{Annotation, Query};
    use crate::db::models::sql::where_node::WhereNode;

    #[test]
    fn new_query_starts_empty() {
        let query = Query::new();

        assert!(query.select.is_empty());
        assert!(query.where_clause.is_none());
        assert!(query.is_empty());
    }

    #[test]
    fn query_is_not_empty_when_select_columns_exist() {
        let mut query = Query::new();
        query.select.push("name".to_string());

        assert!(!query.is_empty());
    }

    #[test]
    fn query_is_not_empty_when_where_clause_exists() {
        let mut query = Query::new();
        query.where_clause = Some(WhereNode::leaf("name", "exact", "Ada"));

        assert!(!query.is_empty());
    }

    #[test]
    fn add_annotation_appends_annotation() {
        let mut query = Query::new();

        query.add_annotation("total", "COUNT(*)");

        assert_eq!(
            query.annotations,
            vec![Annotation {
                alias: "total".to_string(),
                expression: "COUNT(*)".to_string(),
            }]
        );
    }

    #[test]
    fn has_limit_or_offset_detects_limit() {
        let mut query = Query::new();
        query.limit = Some(10);

        assert!(query.has_limit_or_offset());
    }

    #[test]
    fn has_limit_or_offset_detects_offset() {
        let mut query = Query::new();
        query.offset = Some(5);

        assert!(query.has_limit_or_offset());
    }

    #[test]
    fn clear_ordering_removes_all_orderings() {
        let mut query = Query::new();
        query.orderings = vec![
            ("name".to_string(), Order::Asc),
            ("id".to_string(), Order::Desc),
        ];

        query.clear_ordering();

        assert!(query.orderings.is_empty());
    }

    #[test]
    fn clear_limits_removes_limit_and_offset() {
        let mut query = Query {
            limit: Some(10),
            offset: Some(20),
            ..Query::new()
        };

        query.clear_limits();

        assert_eq!(query.limit, None);
        assert_eq!(query.offset, None);
        assert!(!query.has_limit_or_offset());
    }

    #[test]
    fn distinct_flag_can_be_enabled() {
        let query = Query {
            distinct: true,
            ..Query::new()
        };

        assert!(query.distinct);
    }

    #[test]
    fn new_query_defaults_to_non_distinct_and_not_subquery() {
        let query = Query::new();

        assert!(!query.distinct);
        assert!(!query.subquery);
    }
}