This repository has been archived on 2026-05-31. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
libertas/libertas_api/src/extractors/query_options.rs

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()))
}
}