athena_rs 2.9.1

Database gateway API
Documentation
//! Shared query builder helpers for AthenaClient.
use crate::client::AthenaClient;
use crate::client::backend::{BackendError, QueryResult};
use serde_json::Value;

/// Direction used for ordering results.
#[derive(Debug, Clone, Copy)]
pub enum OrderDirection {
    Asc,
    Desc,
}

/// Represents a column-based filter condition.
#[derive(Debug, Clone)]
pub struct Condition {
    pub column: String,
    pub operator: ConditionOperator,
    pub values: Vec<Value>,
}

/// Supported comparison operators.
#[derive(Debug, Clone, Copy)]
pub enum ConditionOperator {
    Eq,
    Neq,
    Gt,
    Lt,
    In,
}

impl Condition {
    pub fn new(column: impl Into<String>, operator: ConditionOperator, values: Vec<Value>) -> Self {
        Self {
            column: column.into(),
            operator,
            values,
        }
    }
}

/// Select query builder.
pub struct SelectBuilder<'a> {
    client: &'a AthenaClient,
    table: String,
    columns: Vec<String>,
    raw_select: Option<String>,
    conditions: Vec<Condition>,
    order_by: Vec<(String, OrderDirection)>,
    limit: Option<usize>,
    offset: Option<usize>,
}

impl<'a> SelectBuilder<'a> {
    pub(crate) fn new(client: &'a AthenaClient, table: impl Into<String>) -> Self {
        Self {
            client,
            table: table.into(),
            columns: Vec::new(),
            raw_select: None,
            conditions: Vec::new(),
            order_by: Vec::new(),
            limit: None,
            offset: None,
        }
    }

    pub fn columns(mut self, columns: impl IntoIterator<Item = impl Into<String>>) -> Self {
        self.columns = columns.into_iter().map(|c| c.into()).collect();
        self
    }

    /// Provide a Supabase/PostgREST-style `select` string with nested relations,
    /// e.g. `id,name,instruments(id,name)`.
    pub fn raw_select(mut self, select: impl Into<String>) -> Self {
        self.raw_select = Some(select.into());
        self
    }

    fn add_condition(
        mut self,
        column: &str,
        operator: ConditionOperator,
        values: Vec<Value>,
    ) -> Self {
        self.conditions
            .push(Condition::new(column, operator, values));
        self
    }

    pub fn where_eq(self, column: &str, value: impl Into<Value>) -> Self {
        self.add_condition(column, ConditionOperator::Eq, vec![value.into()])
    }

    pub fn where_neq(self, column: &str, value: impl Into<Value>) -> Self {
        self.add_condition(column, ConditionOperator::Neq, vec![value.into()])
    }

    pub fn where_gt(self, column: &str, value: impl Into<Value>) -> Self {
        self.add_condition(column, ConditionOperator::Gt, vec![value.into()])
    }

    pub fn where_lt(self, column: &str, value: impl Into<Value>) -> Self {
        self.add_condition(column, ConditionOperator::Lt, vec![value.into()])
    }

    pub fn where_in(self, column: &str, values: Vec<Value>) -> Self {
        self.add_condition(column, ConditionOperator::In, values)
    }

    pub fn order_by(mut self, column: &str, direction: OrderDirection) -> Self {
        self.order_by.push((column.to_string(), direction));
        self
    }

    pub fn limit(mut self, value: usize) -> Self {
        self.limit = Some(value);
        self
    }

    pub fn offset(mut self, value: usize) -> Self {
        self.offset = Some(value);
        self
    }

    pub async fn execute(self) -> Result<QueryResult, BackendError> {
        self.client.execute_select(self).await
    }

    pub(crate) fn into_parts(self) -> SelectQuery {
        SelectQuery {
            table: self.table,
            columns: self.columns,
            raw_select: self.raw_select,
            conditions: self.conditions,
            order_by: self.order_by,
            limit: self.limit,
            offset: self.offset,
        }
    }
}

/// Normalized representation of a select query for translation.
pub(crate) struct SelectQuery {
    pub table: String,
    pub columns: Vec<String>,
    pub raw_select: Option<String>,
    pub conditions: Vec<Condition>,
    pub order_by: Vec<(String, OrderDirection)>,
    pub limit: Option<usize>,
    pub offset: Option<usize>,
}

/// Insert query builder.
pub struct InsertBuilder<'a> {
    client: &'a AthenaClient,
    table: String,
    payload: Value,
}

impl<'a> InsertBuilder<'a> {
    pub(crate) fn new(client: &'a AthenaClient, table: impl Into<String>) -> Self {
        Self {
            client,
            table: table.into(),
            payload: Value::Null,
        }
    }

    pub fn payload(mut self, payload: Value) -> Self {
        self.payload = payload;
        self
    }

    pub async fn execute(self) -> Result<QueryResult, BackendError> {
        self.client.execute_insert(self).await
    }

    pub(crate) fn into_parts(self) -> InsertQuery {
        InsertQuery {
            table: self.table,
            payload: self.payload,
        }
    }
}

pub(crate) struct InsertQuery {
    pub table: String,
    pub payload: Value,
}

/// Update query builder.
pub struct UpdateBuilder<'a> {
    client: &'a AthenaClient,
    table: String,
    row_id: Option<String>,
    payload: Value,
}

impl<'a> UpdateBuilder<'a> {
    pub(crate) fn new(
        client: &'a AthenaClient,
        table: impl Into<String>,
        row_id: Option<String>,
    ) -> Self {
        Self {
            client,
            table: table.into(),
            row_id,
            payload: Value::Null,
        }
    }

    pub fn payload(mut self, payload: Value) -> Self {
        self.payload = payload;
        self
    }

    pub async fn execute(self) -> Result<QueryResult, BackendError> {
        self.client.execute_update(self).await
    }

    pub(crate) fn into_parts(self) -> UpdateQuery {
        UpdateQuery {
            table: self.table,
            row_id: self.row_id,
            payload: self.payload,
        }
    }
}

pub(crate) struct UpdateQuery {
    pub table: String,
    pub row_id: Option<String>,
    pub payload: Value,
}

/// Delete query builder.
pub struct DeleteBuilder<'a> {
    client: &'a AthenaClient,
    table: String,
    row_id: Option<String>,
}

impl<'a> DeleteBuilder<'a> {
    pub(crate) fn new(
        client: &'a AthenaClient,
        table: impl Into<String>,
        row_id: Option<String>,
    ) -> Self {
        Self {
            client,
            table: table.into(),
            row_id,
        }
    }

    pub async fn execute(self) -> Result<QueryResult, BackendError> {
        self.client.execute_delete(self).await
    }

    pub(crate) fn into_parts(self) -> DeleteQuery {
        DeleteQuery {
            table: self.table,
            row_id: self.row_id,
        }
    }
}

pub(crate) struct DeleteQuery {
    pub table: String,
    pub row_id: Option<String>,
}