use chrono::Utc;
use diesel::pg::Pg;
use diesel::{debug_query, sql_query, ExpressionMethods, QueryDsl, QueryResult};
use diesel_async::{AsyncConnection, AsyncPgConnection, RunQueryDsl};
use futures::future::try_join_all;
use futures::Future;
use log::debug;
use uuid::Uuid;
use crate::{
model::{
dto::layers::{LayerDto, LayerRenameDto, LayerSearchParameters},
entity::layers::{Layer, UpdateLayerMarkedDeleted, UpdateLayerName, UpdateLayerOrderIndex},
},
schema::layers,
};
impl Layer {
pub async fn find(
search_parameters: LayerSearchParameters,
conn: &mut AsyncPgConnection,
) -> QueryResult<Vec<LayerDto>> {
let mut query = layers::table.select(layers::all_columns).into_boxed();
if let Some(map_id_search) = search_parameters.map_id {
query = query.filter(layers::map_id.eq(map_id_search));
}
if let Some(type_search) = search_parameters.type_ {
query = query.filter(layers::type_.eq(type_search));
}
if let Some(is_alternative_search) = search_parameters.is_alternative {
query = query.filter(layers::is_alternative.eq(is_alternative_search));
}
if search_parameters.only_non_deleted.is_some() {
query = query.filter(layers::marked_deleted.is_null());
}
query = query.order((layers::order_index, layers::marked_deleted.desc()));
debug!("{}", debug_query::<Pg, _>(&query));
Ok(query
.load::<Self>(conn)
.await?
.into_iter()
.map(Into::into)
.collect())
}
pub async fn find_by_id(id: Uuid, conn: &mut AsyncPgConnection) -> QueryResult<Self> {
let query = layers::table.find(id);
debug!("{}", debug_query::<Pg, _>(&query));
query.first::<Self>(conn).await
}
async fn defer_unique_order_index_constraint(
transaction: &mut AsyncPgConnection,
) -> QueryResult<()> {
sql_query("SET CONSTRAINTS layers_map_id_order_index_unique DEFERRED")
.execute(transaction)
.await?;
Ok(())
}
async fn shift_order_indices_by(
map_id: i32,
order_index: i32,
increment: bool,
transaction: &mut AsyncPgConnection,
) -> QueryResult<()> {
let layer_ids = Self::find(
LayerSearchParameters {
is_alternative: None,
map_id: Some(map_id),
type_: None,
only_non_deleted: Some(()),
},
transaction,
)
.await?;
let updates = layer_ids
.into_iter()
.filter(|l| l.order_index >= order_index)
.map(|l| UpdateLayerOrderIndex {
id: l.id,
order_index: l.order_index + if increment { 1 } else { -1 },
})
.collect();
let futures = Self::do_order_update(updates, transaction);
try_join_all(futures).await?;
Ok(())
}
pub async fn create(
map_id: i32,
new_layer: LayerDto,
conn: &mut AsyncPgConnection,
) -> QueryResult<LayerDto> {
let new_order_index = new_layer.order_index;
let new_layer = Self::from((map_id, new_layer));
let query = diesel::insert_into(layers::table).values(&new_layer);
let created_layer = conn
.transaction(|transaction| {
Box::pin(async move {
Self::shift_order_indices_by(map_id, new_order_index, true, transaction)
.await?;
debug!("{}", debug_query::<Pg, _>(&query));
let layer_dto = query.get_result::<Self>(transaction).await?.into();
Ok::<LayerDto, diesel::result::Error>(layer_dto)
})
})
.await?;
Ok(created_layer)
}
pub async fn reorder(new_order: Vec<Uuid>, conn: &mut AsyncPgConnection) -> QueryResult<()> {
let order_updates: Vec<UpdateLayerOrderIndex> = new_order
.into_iter()
.zip(0..)
.map(|(id, order_index)| UpdateLayerOrderIndex { id, order_index })
.collect();
conn.transaction(|transaction| {
Box::pin(async move {
Self::defer_unique_order_index_constraint(transaction).await?;
let futures = Self::do_order_update(order_updates, transaction);
try_join_all(futures).await?;
Ok::<(), diesel::result::Error>(())
})
})
.await?;
Ok(())
}
fn do_order_update(
updates: Vec<UpdateLayerOrderIndex>,
conn: &mut AsyncPgConnection,
) -> Vec<impl Future<Output = QueryResult<usize>>> {
updates
.into_iter()
.map(|update| {
diesel::update(layers::table.find(update.id))
.set(update)
.execute(conn)
})
.collect()
}
pub async fn rename(dto: LayerRenameDto, conn: &mut AsyncPgConnection) -> QueryResult<()> {
let update: UpdateLayerName = dto.into();
let query = diesel::update(layers::table.find(update.id)).set(&update);
debug!("{}", debug_query::<Pg, _>(&query));
query.execute(conn).await?;
Ok(())
}
async fn set_marked_deleted(
drawing_layer_id: Uuid,
marked_deleted: bool,
conn: &mut AsyncPgConnection,
) -> QueryResult<Self> {
let marked_deleted_timestamp = marked_deleted.then(|| Utc::now().naive_utc());
diesel::update(layers::table.find(drawing_layer_id))
.set(UpdateLayerMarkedDeleted {
id: drawing_layer_id,
marked_deleted: marked_deleted_timestamp,
})
.get_result::<Self>(conn)
.await
}
pub async fn delete(
map_id: i32,
layer_id: Uuid,
conn: &mut AsyncPgConnection,
) -> QueryResult<()> {
conn.transaction(|transaction| {
Box::pin(async move {
let layer = Self::set_marked_deleted(layer_id, true, transaction).await?;
Self::shift_order_indices_by(map_id, layer.order_index, false, transaction).await?;
Ok(())
})
})
.await
}
pub async fn restore(
map_id: i32,
layer_id: Uuid,
conn: &mut AsyncPgConnection,
) -> QueryResult<()> {
conn.transaction(|transaction| {
Box::pin(async move {
Self::defer_unique_order_index_constraint(transaction).await?;
let layer = Self::find_by_id(layer_id, transaction).await?;
Self::shift_order_indices_by(map_id, layer.order_index, true, transaction).await?;
_ = Self::set_marked_deleted(layer_id, false, transaction).await?;
Ok(())
})
})
.await
}
}