use super::QueryContext;
use crate::{
error::Error,
model::{Model, Mutation, Query},
Map,
};
use std::borrow::Cow;
pub trait ModelHooks: Model {
type Data: Default = ();
type Extension: Clone + Send + Sync + 'static = ();
#[inline]
async fn before_extract() -> Result<(), Error> {
Ok(())
}
#[inline]
async fn after_extract(&mut self, _extension: Self::Extension) -> Result<(), Error> {
Ok(())
}
#[inline]
async fn before_validation(
_model: &mut Map,
_extension: Option<&Self::Extension>,
) -> Result<(), Error> {
Ok(())
}
#[inline]
async fn after_validation(&mut self, _model: &mut Map) -> Result<(), Error> {
Ok(())
}
#[inline]
async fn before_create_table() -> Result<(), Error> {
Ok(())
}
#[inline]
async fn after_create_table() -> Result<(), Error> {
Ok(())
}
#[inline]
async fn before_scan(query: &str) -> Result<QueryContext, Error> {
let ctx = QueryContext::new();
let query_id = ctx.query_id().to_string();
tracing::debug!(query_id, query);
Ok(ctx)
}
async fn after_scan(ctx: &QueryContext) -> Result<(), Error> {
let query_id = ctx.query_id().to_string();
let query = ctx.query();
let arguments = ctx.format_arguments();
let message = match ctx.rows_affected() {
Some(0) => Cow::Borrowed("no rows affected or fetched"),
Some(1) => Cow::Borrowed("only one row affected or fetched"),
Some(num_rows) if num_rows > 1 => {
Cow::Owned(format!("{num_rows} rows affected or fetched"))
}
_ => Cow::Borrowed("the query result has not been recorded"),
};
let execution_time = ctx.start_time().elapsed();
let execution_time_millis = execution_time.as_millis();
if execution_time_millis > 3000 {
tracing::warn!(
query_id,
query,
arguments,
execution_time_millis,
"{message}"
);
} else if execution_time_millis > 1000 {
tracing::info!(
query_id,
query,
arguments,
execution_time_millis,
"{message}"
);
} else {
tracing::debug!(
query_id,
query,
arguments,
execution_time_micros = execution_time.as_micros(),
"{message}"
);
}
Ok(())
}
#[inline]
async fn before_insert(&mut self) -> Result<Self::Data, Error> {
Ok(Self::Data::default())
}
#[inline]
async fn after_insert(ctx: &QueryContext, _data: Self::Data) -> Result<(), Error> {
if !ctx.is_success() {
ctx.record_error("fail to insert a model into the table");
}
ctx.emit_metrics("insert");
Ok(())
}
#[inline]
async fn before_delete(&mut self) -> Result<Self::Data, Error> {
Ok(Self::Data::default())
}
#[inline]
async fn after_delete(self, ctx: &QueryContext, _data: Self::Data) -> Result<(), Error> {
let query = ctx.query();
let query_id = ctx.query_id().to_string();
if ctx.is_success() {
tracing::warn!(query, query_id, "a model was deleted from the table");
} else {
tracing::error!(query, query_id, "fail to detele a model from the table");
}
ctx.emit_metrics("delete");
Ok(())
}
#[inline]
async fn before_soft_delete(&mut self) -> Result<Self::Data, Error> {
Ok(Self::Data::default())
}
#[inline]
async fn after_soft_delete(ctx: &QueryContext, _data: Self::Data) -> Result<(), Error> {
if !ctx.is_success() {
ctx.record_error("fail to logically delete a model from the table");
}
ctx.emit_metrics("soft_delete");
Ok(())
}
#[inline]
async fn before_lock(&mut self) -> Result<Self::Data, Error> {
Ok(Self::Data::default())
}
#[inline]
async fn after_lock(ctx: &QueryContext, _data: Self::Data) -> Result<(), Error> {
if !ctx.is_success() {
ctx.record_error("fail to lock a model in the table");
}
ctx.emit_metrics("lock");
Ok(())
}
#[inline]
async fn before_update(&mut self) -> Result<Self::Data, Error> {
Ok(Self::Data::default())
}
#[inline]
async fn after_update(ctx: &QueryContext, _data: Self::Data) -> Result<(), Error> {
if !ctx.is_success() {
ctx.record_error("fail to update a model in the table");
}
ctx.emit_metrics("update");
Ok(())
}
#[inline]
async fn before_upsert(&mut self) -> Result<Self::Data, Error> {
Ok(Self::Data::default())
}
#[inline]
async fn after_upsert(ctx: &QueryContext, _data: Self::Data) -> Result<(), Error> {
if !ctx.is_success() {
ctx.record_error("fail to upsert a model into the table");
}
ctx.emit_metrics("upsert");
Ok(())
}
#[inline]
async fn before_count(_query: &Query) -> Result<(), Error> {
Ok(())
}
#[inline]
async fn after_count(ctx: &QueryContext) -> Result<(), Error> {
if !ctx.is_success() {
ctx.record_error("fail to count the models in the table");
}
ctx.emit_metrics("count");
Ok(())
}
#[inline]
async fn before_query(_query: &Query) -> Result<(), Error> {
Ok(())
}
#[inline]
async fn after_query(ctx: &QueryContext) -> Result<(), Error> {
if !ctx.is_success() {
ctx.record_error("fail to select the models from the table");
}
ctx.emit_metrics("query");
Ok(())
}
#[inline]
async fn before_mutation(_query: &Query, _mutation: &mut Mutation) -> Result<(), Error> {
Ok(())
}
#[inline]
async fn after_mutation(ctx: &QueryContext) -> Result<(), Error> {
if !ctx.is_success() {
ctx.record_error("fail to update the models in the table");
}
ctx.emit_metrics("mutation");
Ok(())
}
#[inline]
async fn before_list(
_query: &mut Query,
_extension: Option<&Self::Extension>,
) -> Result<(), Error> {
Ok(())
}
#[inline]
async fn after_decode(_model: &mut Map) -> Result<(), Error> {
Ok(())
}
#[inline]
async fn before_respond(
_model: &mut Map,
_extension: Option<&Self::Extension>,
) -> Result<(), Error> {
Ok(())
}
}