150 lines
4.6 KiB
Rust
150 lines
4.6 KiB
Rust
use axum::{
|
|
extract::{FromRequestParts, Query},
|
|
http::request::Parts,
|
|
};
|
|
use libertas_core::{
|
|
error::CoreError,
|
|
schema::{
|
|
FilterCondition, FilterOperator, FilterParams, ListMediaOptions, MetadataFilter,
|
|
PaginationParams, SortOrder, SortParams,
|
|
},
|
|
};
|
|
|
|
use crate::{error::ApiError, schema::ListMediaParams, state::AppState};
|
|
|
|
pub struct ApiListMediaOptions(pub ListMediaOptions);
|
|
|
|
const DEFAULT_PAGE: u32 = 1;
|
|
const DEFAULT_LIMIT: u32 = 50;
|
|
|
|
impl From<ListMediaParams> for ListMediaOptions {
|
|
fn from(params: ListMediaParams) -> Self {
|
|
let sort = params.sort_by.map(|field| {
|
|
let order = match params.order.as_deref() {
|
|
Some("asc") => SortOrder::Asc,
|
|
_ => SortOrder::Desc,
|
|
};
|
|
SortParams {
|
|
sort_by: field,
|
|
sort_order: order,
|
|
}
|
|
});
|
|
|
|
let metadata_filters = if params.metadata.is_empty() {
|
|
None
|
|
} else {
|
|
Some(
|
|
params
|
|
.metadata
|
|
.into_iter()
|
|
.filter_map(|s| {
|
|
s.split_once(":").map(|(key, value)| MetadataFilter {
|
|
tag_name: key.to_string(),
|
|
tag_value: value.to_string(),
|
|
})
|
|
})
|
|
.collect::<Vec<_>>(),
|
|
)
|
|
};
|
|
|
|
let pagination = {
|
|
let page = params.page.unwrap_or(DEFAULT_PAGE);
|
|
let limit = params.limit.unwrap_or(DEFAULT_LIMIT);
|
|
|
|
let page = if page == 0 { DEFAULT_PAGE } else { page };
|
|
|
|
let limit = if limit > (DEFAULT_LIMIT * 2) {
|
|
DEFAULT_LIMIT * 2
|
|
} else {
|
|
limit
|
|
};
|
|
|
|
Some(PaginationParams { page, limit })
|
|
};
|
|
|
|
let conditions = if params.filters.is_empty() {
|
|
None
|
|
} else {
|
|
let mut conds = Vec::new();
|
|
for filter_str in params.filters {
|
|
let parts: Vec<&str> = filter_str.splitn(3, ':').collect();
|
|
if parts.len() == 3 {
|
|
let field = parts[0].to_string();
|
|
let op_str = parts[1];
|
|
let value = parts[2].to_string();
|
|
|
|
let operator = match op_str.to_lowercase().as_str() {
|
|
"eq" => Some(FilterOperator::Eq),
|
|
"neq" => Some(FilterOperator::Neq),
|
|
"like" => Some(FilterOperator::Like),
|
|
"gt" => Some(FilterOperator::Gt),
|
|
"lt" => Some(FilterOperator::Lt),
|
|
"gte" => Some(FilterOperator::Gte),
|
|
"lte" => Some(FilterOperator::Lte),
|
|
_ => None,
|
|
};
|
|
|
|
if let Some(op) = operator {
|
|
conds.push(FilterCondition {
|
|
field,
|
|
operator: op,
|
|
value,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
if conds.is_empty() { None } else { Some(conds) }
|
|
};
|
|
|
|
let filter = Some(FilterParams {
|
|
mime_type: params.mime_type,
|
|
metadata_filters,
|
|
conditions,
|
|
});
|
|
|
|
ListMediaOptions {
|
|
sort,
|
|
filter,
|
|
pagination,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FromRequestParts<AppState> for ApiListMediaOptions {
|
|
type Rejection = ApiError;
|
|
|
|
async fn from_request_parts(
|
|
parts: &mut Parts,
|
|
state: &AppState,
|
|
) -> Result<Self, Self::Rejection> {
|
|
let Query(raw_params) = Query::<Vec<(String, String)>>::from_request_parts(parts, state)
|
|
.await
|
|
.map_err(|e| ApiError::from(CoreError::Validation(e.to_string())))?;
|
|
|
|
let mut params = ListMediaParams {
|
|
sort_by: None,
|
|
order: None,
|
|
mime_type: None,
|
|
metadata: Vec::new(),
|
|
filters: Vec::new(),
|
|
page: None,
|
|
limit: None,
|
|
};
|
|
|
|
for (key, value) in raw_params {
|
|
match key.as_str() {
|
|
"sort_by" => params.sort_by = Some(value),
|
|
"order" => params.order = Some(value),
|
|
"mime_type" => params.mime_type = Some(value),
|
|
"metadata" => params.metadata.push(value),
|
|
"filters" => params.filters.push(value),
|
|
"page" => params.page = value.parse().ok(),
|
|
"limit" => params.limit = value.parse().ok(),
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
Ok(ApiListMediaOptions(params.into()))
|
|
}
|
|
}
|