use std::fmt::Debug;
use vantage_core::{Result, VantageError, error};
use vantage_table::table::Table;
use vantage_table::traits::table_source::TableSource;
use vantage_types::{Entity, Record};
use crate::set_writes::{SetWrites, id_exists_anywhere, row_in_set};
pub(crate) async fn guard_insert<T, E>(
db: &T,
table: &Table<T, E>,
id: &T::Id,
record: &Record<T::Value>,
) -> Result<Option<Record<T::Value>>>
where
T: SetWrites,
E: Entity<T::Value>,
{
if let Some(existing) = db.get_table_value(table, id).await? {
return Ok(Some(existing));
}
if table.conditions().next().is_some() {
if id_exists_anywhere(db, table, id).await? {
return Err(held_outside(table, id));
}
require_fits(db, table, id, record).await?;
}
Ok(None)
}
pub(crate) async fn settle_failed_insert<T, E>(
db: &T,
table: &Table<T, E>,
id: &T::Id,
err: VantageError,
) -> Result<Record<T::Value>>
where
T: SetWrites,
E: Entity<T::Value>,
{
if let Some(existing) = db.get_table_value(table, id).await? {
return Ok(existing);
}
if id_exists_anywhere(db, table, id).await? {
return Err(held_outside(table, id));
}
Err(err)
}
pub(crate) async fn guard_replace<T, E>(
db: &T,
table: &Table<T, E>,
id: &T::Id,
record: &Record<T::Value>,
) -> Result<()>
where
T: SetWrites,
E: Entity<T::Value>,
{
if table.conditions().next().is_none() {
return Ok(());
}
let in_set = db.get_table_value(table, id).await?.is_some();
if !in_set && id_exists_anywhere(db, table, id).await? {
return Err(held_outside(table, id));
}
require_fits(db, table, id, record).await
}
pub(crate) async fn guard_patch<T, E>(
db: &T,
table: &Table<T, E>,
id: &T::Id,
partial: &Record<T::Value>,
) -> Result<()>
where
T: SetWrites,
E: Entity<T::Value>,
{
let Some(mut merged) = db.get_table_value(table, id).await? else {
return Err(patch_not_found(table, id));
};
if table.conditions().next().is_some() {
for (k, v) in partial.iter() {
merged.insert(k.clone(), v.clone());
}
if !row_in_set(db, table, &merged).await? {
return Err(patch_leaves_set(table, id));
}
}
Ok(())
}
pub(crate) async fn guard_new_row<T, E>(
db: &T,
table: &Table<T, E>,
record: &Record<T::Value>,
) -> Result<()>
where
T: SetWrites,
E: Entity<T::Value>,
{
if !row_in_set(db, table, record).await? {
return Err(new_row_not_in_set(table));
}
Ok(())
}
async fn require_fits<T, E>(
db: &T,
table: &Table<T, E>,
id: &T::Id,
record: &Record<T::Value>,
) -> Result<()>
where
T: SetWrites,
E: Entity<T::Value>,
{
let mut row = record.clone();
row.insert(table.id_field_name(), T::id_cell(table, id));
if !row_in_set(db, table, &row).await? {
return Err(not_in_set(table, id));
}
Ok(())
}
pub(crate) fn held_outside<T, E>(table: &Table<T, E>, id: &T::Id) -> VantageError
where
T: TableSource,
T::Id: Debug,
E: Entity<T::Value>,
{
error!(
"id is held by a row outside this set",
table = table.table_name(),
id = id
)
.mark_conflict()
}
pub(crate) fn not_in_set<T, E>(table: &Table<T, E>, id: &T::Id) -> VantageError
where
T: TableSource,
T::Id: Debug,
E: Entity<T::Value>,
{
error!(
"record does not belong to this set",
table = table.table_name(),
id = id
)
.mark_conflict()
}
pub(crate) fn patch_leaves_set<T, E>(table: &Table<T, E>, id: &T::Id) -> VantageError
where
T: TableSource,
T::Id: Debug,
E: Entity<T::Value>,
{
error!(
"patch would move the row out of this set",
table = table.table_name(),
id = id
)
.mark_conflict()
}
pub(crate) fn patch_not_found<T, E>(table: &Table<T, E>, id: &T::Id) -> VantageError
where
T: TableSource,
T::Id: Debug,
E: Entity<T::Value>,
{
error!("Row not found", table = table.table_name(), id = id).mark_not_found()
}
pub(crate) fn new_row_not_in_set<T, E>(table: &Table<T, E>) -> VantageError
where
T: TableSource,
E: Entity<T::Value>,
{
error!(
"record does not belong to this set",
table = table.table_name()
)
.mark_conflict()
}
pub(crate) async fn refetch_after_patch<T, E>(
source: &T,
table: &Table<T, E>,
id: &T::Id,
) -> Result<Record<T::Value>>
where
T: TableSource,
T::Id: Debug,
E: Entity<T::Value>,
{
source
.get_table_value(table, id)
.await?
.ok_or_else(|| error!("Row not found after patch", id = id.clone()).mark_not_found())
}