use super::{LogicCondition, OrderTerm, ParsedParams, PreferOptions, RpcParams, SelectItem};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Operation {
Select(ParsedParams, Option<PreferOptions>),
Insert(InsertParams, Option<PreferOptions>),
Update(UpdateParams, Option<PreferOptions>),
Delete(DeleteParams, Option<PreferOptions>),
Rpc(RpcParams, Option<PreferOptions>),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ResolvedTable {
pub schema: String,
pub name: String,
}
impl ResolvedTable {
pub fn new(schema: impl Into<String>, name: impl Into<String>) -> Self {
Self {
schema: schema.into(),
name: name.into(),
}
}
pub fn qualified_name(&self) -> String {
format!("\"{}\".\"{}\"", self.schema, self.name)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct InsertParams {
pub values: InsertValues,
pub columns: Option<Vec<String>>,
pub on_conflict: Option<OnConflict>,
pub returning: Option<Vec<SelectItem>>,
}
impl InsertParams {
pub fn new(values: InsertValues) -> Self {
Self {
values,
columns: None,
on_conflict: None,
returning: None,
}
}
pub fn with_columns(mut self, columns: Vec<String>) -> Self {
self.columns = Some(columns);
self
}
pub fn with_on_conflict(mut self, on_conflict: OnConflict) -> Self {
self.on_conflict = Some(on_conflict);
self
}
pub fn with_returning(mut self, returning: Vec<SelectItem>) -> Self {
self.returning = Some(returning);
self
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum InsertValues {
Single(HashMap<String, serde_json::Value>),
Bulk(Vec<HashMap<String, serde_json::Value>>),
}
impl InsertValues {
pub fn len(&self) -> usize {
match self {
InsertValues::Single(_) => 1,
InsertValues::Bulk(rows) => rows.len(),
}
}
pub fn is_empty(&self) -> bool {
match self {
InsertValues::Single(map) => map.is_empty(),
InsertValues::Bulk(rows) => rows.is_empty(),
}
}
pub fn get_columns(&self) -> Vec<String> {
match self {
InsertValues::Single(map) => {
let mut cols: Vec<String> = map.keys().cloned().collect();
cols.sort();
cols
}
InsertValues::Bulk(rows) => {
if let Some(first) = rows.first() {
let mut cols: Vec<String> = first.keys().cloned().collect();
cols.sort();
cols
} else {
Vec::new()
}
}
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct OnConflict {
pub columns: Vec<String>,
pub action: ConflictAction,
pub where_clause: Option<Vec<LogicCondition>>,
pub update_columns: Option<Vec<String>>,
}
impl OnConflict {
pub fn do_nothing(columns: Vec<String>) -> Self {
Self {
columns,
action: ConflictAction::DoNothing,
where_clause: None,
update_columns: None,
}
}
pub fn do_update(columns: Vec<String>) -> Self {
Self {
columns,
action: ConflictAction::DoUpdate,
where_clause: None,
update_columns: None,
}
}
pub fn with_where_clause(mut self, where_clause: Vec<LogicCondition>) -> Self {
self.where_clause = Some(where_clause);
self
}
pub fn with_update_columns(mut self, update_columns: Vec<String>) -> Self {
self.update_columns = Some(update_columns);
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ConflictAction {
DoNothing,
DoUpdate,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct UpdateParams {
pub set_values: HashMap<String, serde_json::Value>,
pub filters: Vec<LogicCondition>,
pub order: Vec<OrderTerm>,
pub limit: Option<u64>,
pub returning: Option<Vec<SelectItem>>,
}
impl UpdateParams {
pub fn new(set_values: HashMap<String, serde_json::Value>) -> Self {
Self {
set_values,
filters: Vec::new(),
order: Vec::new(),
limit: None,
returning: None,
}
}
pub fn with_filters(mut self, filters: Vec<LogicCondition>) -> Self {
self.filters = filters;
self
}
pub fn with_order(mut self, order: Vec<OrderTerm>) -> Self {
self.order = order;
self
}
pub fn with_limit(mut self, limit: u64) -> Self {
self.limit = Some(limit);
self
}
pub fn with_returning(mut self, returning: Vec<SelectItem>) -> Self {
self.returning = Some(returning);
self
}
pub fn has_filters(&self) -> bool {
!self.filters.is_empty()
}
pub fn is_set_empty(&self) -> bool {
self.set_values.is_empty()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DeleteParams {
pub filters: Vec<LogicCondition>,
pub order: Vec<OrderTerm>,
pub limit: Option<u64>,
pub returning: Option<Vec<SelectItem>>,
}
impl DeleteParams {
pub fn new() -> Self {
Self {
filters: Vec::new(),
order: Vec::new(),
limit: None,
returning: None,
}
}
pub fn with_filters(mut self, filters: Vec<LogicCondition>) -> Self {
self.filters = filters;
self
}
pub fn with_order(mut self, order: Vec<OrderTerm>) -> Self {
self.order = order;
self
}
pub fn with_limit(mut self, limit: u64) -> Self {
self.limit = Some(limit);
self
}
pub fn with_returning(mut self, returning: Vec<SelectItem>) -> Self {
self.returning = Some(returning);
self
}
pub fn has_filters(&self) -> bool {
!self.filters.is_empty()
}
}
impl Default for DeleteParams {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_resolved_table_new() {
let table = ResolvedTable::new("public", "users");
assert_eq!(table.schema, "public");
assert_eq!(table.name, "users");
}
#[test]
fn test_resolved_table_qualified_name() {
let table = ResolvedTable::new("auth", "users");
assert_eq!(table.qualified_name(), "\"auth\".\"users\"");
}
#[test]
fn test_insert_params_new() {
let mut map = HashMap::new();
map.insert("name".to_string(), json!("Alice"));
let params = InsertParams::new(InsertValues::Single(map));
assert!(params.columns.is_none());
assert!(params.on_conflict.is_none());
assert!(params.returning.is_none());
}
#[test]
fn test_insert_params_with_columns() {
let mut map = HashMap::new();
map.insert("name".to_string(), json!("Alice"));
let params = InsertParams::new(InsertValues::Single(map))
.with_columns(vec!["name".to_string(), "email".to_string()]);
assert_eq!(params.columns.as_ref().unwrap().len(), 2);
}
#[test]
fn test_insert_params_with_on_conflict() {
let mut map = HashMap::new();
map.insert("email".to_string(), json!("alice@example.com"));
let conflict = OnConflict::do_update(vec!["email".to_string()]);
let params = InsertParams::new(InsertValues::Single(map)).with_on_conflict(conflict);
assert!(params.on_conflict.is_some());
assert_eq!(params.on_conflict.unwrap().action, ConflictAction::DoUpdate);
}
#[test]
fn test_insert_values_single_len() {
let mut map = HashMap::new();
map.insert("name".to_string(), json!("Alice"));
let values = InsertValues::Single(map);
assert_eq!(values.len(), 1);
assert!(!values.is_empty());
}
#[test]
fn test_insert_values_bulk_len() {
let mut map1 = HashMap::new();
map1.insert("name".to_string(), json!("Alice"));
let mut map2 = HashMap::new();
map2.insert("name".to_string(), json!("Bob"));
let values = InsertValues::Bulk(vec![map1, map2]);
assert_eq!(values.len(), 2);
assert!(!values.is_empty());
}
#[test]
fn test_insert_values_get_columns() {
let mut map = HashMap::new();
map.insert("name".to_string(), json!("Alice"));
map.insert("age".to_string(), json!(30));
let values = InsertValues::Single(map);
let columns = values.get_columns();
assert_eq!(columns.len(), 2);
assert!(columns.contains(&"name".to_string()));
assert!(columns.contains(&"age".to_string()));
}
#[test]
fn test_on_conflict_do_nothing() {
let conflict = OnConflict::do_nothing(vec!["email".to_string()]);
assert_eq!(conflict.columns.len(), 1);
assert_eq!(conflict.action, ConflictAction::DoNothing);
}
#[test]
fn test_on_conflict_do_update() {
let conflict = OnConflict::do_update(vec!["email".to_string()]);
assert_eq!(conflict.action, ConflictAction::DoUpdate);
}
#[test]
fn test_update_params_new() {
let mut map = HashMap::new();
map.insert("status".to_string(), json!("active"));
let params = UpdateParams::new(map);
assert_eq!(params.set_values.len(), 1);
assert!(params.filters.is_empty());
assert!(params.order.is_empty());
assert!(params.limit.is_none());
}
#[test]
fn test_update_params_with_limit() {
let mut map = HashMap::new();
map.insert("status".to_string(), json!("active"));
let params = UpdateParams::new(map).with_limit(10);
assert_eq!(params.limit, Some(10));
}
#[test]
fn test_update_params_has_filters() {
let mut map = HashMap::new();
map.insert("status".to_string(), json!("active"));
let params = UpdateParams::new(map);
assert!(!params.has_filters());
}
#[test]
fn test_update_params_is_set_empty() {
let params = UpdateParams::new(HashMap::new());
assert!(params.is_set_empty());
}
#[test]
fn test_delete_params_new() {
let params = DeleteParams::new();
assert!(params.filters.is_empty());
assert!(params.order.is_empty());
assert!(params.limit.is_none());
assert!(params.returning.is_none());
}
#[test]
fn test_delete_params_with_limit() {
let params = DeleteParams::new().with_limit(5);
assert_eq!(params.limit, Some(5));
}
#[test]
fn test_delete_params_has_filters() {
let params = DeleteParams::new();
assert!(!params.has_filters());
}
#[test]
fn test_delete_params_default() {
let params = DeleteParams::default();
assert!(params.filters.is_empty());
}
#[test]
fn test_resolved_table_serialization() {
let table = ResolvedTable::new("public", "users");
let json = serde_json::to_string(&table).unwrap();
assert!(json.contains("public"));
assert!(json.contains("users"));
}
#[test]
fn test_insert_params_serialization() {
let mut map = HashMap::new();
map.insert("name".to_string(), json!("Alice"));
let params = InsertParams::new(InsertValues::Single(map));
let json = serde_json::to_string(¶ms).unwrap();
assert!(json.contains("Alice"));
}
#[test]
fn test_update_params_serialization() {
let mut map = HashMap::new();
map.insert("status".to_string(), json!("active"));
let params = UpdateParams::new(map);
let json = serde_json::to_string(¶ms).unwrap();
assert!(json.contains("active"));
}
#[test]
fn test_delete_params_serialization() {
let params = DeleteParams::new();
let json = serde_json::to_string(¶ms).unwrap();
assert!(json.contains("filters"));
}
#[test]
fn test_conflict_action_serialization() {
let action = ConflictAction::DoNothing;
let json = serde_json::to_string(&action).unwrap();
assert!(json.contains("DoNothing"));
}
#[test]
fn test_insert_values_empty_map() {
let values = InsertValues::Single(HashMap::new());
assert!(values.is_empty());
assert_eq!(values.len(), 1); }
#[test]
fn test_insert_values_empty_bulk() {
let values = InsertValues::Bulk(Vec::new());
assert!(values.is_empty());
assert_eq!(values.len(), 0);
}
#[test]
fn test_bulk_insert_get_columns() {
let mut map1 = HashMap::new();
map1.insert("name".to_string(), json!("Alice"));
map1.insert("age".to_string(), json!(30));
let mut map2 = HashMap::new();
map2.insert("name".to_string(), json!("Bob"));
map2.insert("age".to_string(), json!(25));
let values = InsertValues::Bulk(vec![map1, map2]);
let columns = values.get_columns();
assert_eq!(columns.len(), 2);
}
}