use async_trait::async_trait;
use hyper::Method;
use reinhardt_core::exception::{Error, Result};
use reinhardt_db::orm::{Filter, FilterOperator, FilterValue, Manager, Model, QuerySet};
use reinhardt_http::{Request, Response};
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use crate::core::View;
pub struct DestroyAPIView<M>
where
M: Model + Serialize + for<'de> Deserialize<'de> + Send + Sync + Clone,
{
queryset: Option<QuerySet<M>>,
lookup_field: String,
_model: PhantomData<M>,
}
impl<M> DestroyAPIView<M>
where
M: Model + Serialize + for<'de> Deserialize<'de> + Send + Sync + Clone + 'static,
{
pub fn new() -> Self {
Self {
queryset: None,
lookup_field: "pk".to_string(),
_model: PhantomData,
}
}
pub fn with_queryset(mut self, queryset: QuerySet<M>) -> Self {
self.queryset = Some(queryset);
self
}
pub fn with_lookup_field(mut self, field: String) -> Self {
self.lookup_field = field;
self
}
fn get_queryset(&self) -> QuerySet<M> {
self.queryset.clone().unwrap_or_default()
}
async fn get_object(&self, request: &Request) -> Result<M>
where
M: serde::de::DeserializeOwned,
{
let lookup_value = request.path_params.get(&self.lookup_field).ok_or_else(|| {
Error::Http(format!(
"Missing lookup field '{}' in path parameters",
self.lookup_field
))
})?;
let filter_value = if let Ok(int_value) = lookup_value.parse::<i64>() {
FilterValue::Integer(int_value)
} else {
FilterValue::String(lookup_value.clone())
};
let filter = Filter::new(self.lookup_field.clone(), FilterOperator::Eq, filter_value);
self.get_queryset()
.filter(filter)
.get()
.await
.map_err(|e| Error::Http(format!("Object not found: {}", e)))
}
async fn perform_destroy(&self, request: &Request) -> Result<()>
where
M: serde::de::DeserializeOwned,
{
let object = self.get_object(request).await?;
let pk = object
.primary_key()
.ok_or_else(|| Error::Http("Object has no primary key".to_string()))?;
let manager = Manager::<M>::new();
manager
.delete(pk)
.await
.map_err(|e| Error::Http(format!("Failed to delete: {}", e)))
}
}
impl<M> Default for DestroyAPIView<M>
where
M: Model + Serialize + for<'de> Deserialize<'de> + Send + Sync + Clone + 'static,
{
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl<M> View for DestroyAPIView<M>
where
M: Model + Serialize + for<'de> Deserialize<'de> + Send + Sync + Clone + 'static,
{
async fn dispatch(&self, request: Request) -> Result<Response> {
match request.method {
Method::DELETE => {
self.perform_destroy(&request).await?;
Ok(Response::no_content())
}
_ => Err(Error::MethodNotAllowed(format!(
"Method {} not allowed",
request.method
))),
}
}
fn allowed_methods(&self) -> Vec<&'static str> {
vec!["DELETE", "OPTIONS"]
}
}