Current section
Files
Jump to
Current section
Files
src/db/query/yum.gleam
import db/schema
import gleam/bool
import gleam/dynamic
import gleam/int
import gleam/list
import gleam/result
import gleam/string
pub type QueryBuilder {
QueryBuilder(
table: schema.Table,
select_columns: List(String),
where_clauses: List(String),
order_by_clauses: List(String),
)
}
pub type WhereBuilder {
WhereBuilder(table: schema.Table, clauses: List(String))
}
// Value type
pub type Value {
IntValue(Int)
StringValue(String)
BoolValue(Bool)
ErrorValue(String)
}
// Types for equalities
pub type Eq {
Equals(col: String, value: Value)
}
// Function to turn to clause
fn to_clause_string(eq: Eq) -> String {
case eq.value {
StringValue(value) -> eq.col <> " = '" <> value <> "'"
BoolValue(value) -> eq.col <> " = " <> bool.to_string(value)
IntValue(value) -> eq.col <> " = " <> int.to_string(value)
ErrorValue(value) -> panic
}
}
fn to_clause(table: schema.Table, eq: Eq) {
let col = list.find(table.columns, fn(c) { c.name == eq.col })
case is_col_type(table, eq.col, eq.value) {
True -> to_clause_string(eq)
False -> "FAILED!"
}
}
// helper function to check if value is right value
fn is_right_value(value: Value, target: Value) -> Bool {
case value, target {
IntValue(_), IntValue(_) -> True
StringValue(_), StringValue(_) -> True
BoolValue(_), BoolValue(_) -> True
_, _ -> False
}
}
// Function to type check that values are equal to their column type
fn is_col_type(table: schema.Table, col: String, value: Value) {
let assert Ok(col) = list.find(table.columns, fn(c) { c.name == col })
case col {
schema.StringColumn(_, _) -> is_right_value(value, StringValue("Test"))
schema.BoolColumn(_) -> is_right_value(value, BoolValue(True))
schema.ForeignKeyColumn(_, _, _, _) -> is_right_value(value, IntValue(1))
// assumption TODO
schema.IntColumn(_) -> is_right_value(value, IntValue(1))
schema.SerialColumn(_) -> is_right_value(value, IntValue(1))
// assumption TODO
}
}
// Wrap value
fn wrap_value(value: dynamic.Dynamic) -> Value {
case dynamic.bool(value) {
Ok(value) -> BoolValue(value)
Error(_) -> {
case dynamic.int(value) {
Ok(value) -> IntValue(value)
Error(_) -> {
case dynamic.string(value) {
Ok(value) -> StringValue(value)
Error(_) -> ErrorValue("Not a supported value!")
}
}
}
}
}
}
pub fn equals(builder: WhereBuilder, col: String, value: v) -> WhereBuilder {
WhereBuilder(
table: builder.table,
clauses: list.append(builder.clauses, [
to_clause(builder.table, Equals(col, wrap_value(dynamic.from(value)))),
]),
)
}
pub fn ere(table: schema.Table) {
WhereBuilder(table, [])
}
pub fn to_where(builder: WhereBuilder) {
builder.clauses
}
// Initialize a new query builder for a table
pub fn new(table: schema.Table) -> QueryBuilder {
QueryBuilder(table, [], [], [])
}
// Add a select clause (selecting all columns by default)
pub fn select(builder: QueryBuilder) -> QueryBuilder {
QueryBuilder(
builder.table,
list.map(builder.table.columns, fn(c) { c.name }),
builder.where_clauses,
builder.order_by_clauses,
)
}
// Add a where clause
pub fn wh(builder: QueryBuilder, condition: List(String)) -> QueryBuilder {
QueryBuilder(
builder.table,
builder.select_columns,
list.append(builder.where_clauses, condition),
builder.order_by_clauses,
)
}
// Add an order by clause
pub fn order_by(
builder: QueryBuilder,
column: String,
direction: String,
) -> QueryBuilder {
QueryBuilder(
builder.table,
builder.select_columns,
builder.where_clauses,
list.append(builder.order_by_clauses, [column <> " " <> direction]),
)
}
// Build the final SQL query
pub fn to_sql(builder: QueryBuilder) -> String {
let base_query =
"SELECT "
<> string.join(builder.select_columns, ", ")
<> " FROM "
<> builder.table.name
let where_sql = case builder.where_clauses {
[] -> ""
clauses -> " WHERE " <> string.join(clauses, " AND ")
}
let order_by_sql = case builder.order_by_clauses {
[] -> ""
clauses -> " ORDER BY " <> string.join(clauses, ", ")
}
base_query <> where_sql <> order_by_sql <> ";"
}