diff --git a/crates/oxidetalis/src/main.rs b/crates/oxidetalis/src/main.rs index 3b373a0..637cefc 100644 --- a/crates/oxidetalis/src/main.rs +++ b/crates/oxidetalis/src/main.rs @@ -30,6 +30,7 @@ mod extensions; mod macros; mod middlewares; mod nonce; +mod parameters; mod routes; mod schemas; mod utils; diff --git a/crates/oxidetalis/src/parameters/mod.rs b/crates/oxidetalis/src/parameters/mod.rs new file mode 100644 index 0000000..4fea6e9 --- /dev/null +++ b/crates/oxidetalis/src/parameters/mod.rs @@ -0,0 +1,21 @@ +// OxideTalis Messaging Protocol homeserver implementation +// Copyright (C) 2024 OxideTalis Developers +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +//! Set of route parameters for the API + +mod pagination; + +pub use pagination::*; diff --git a/crates/oxidetalis/src/parameters/pagination.rs b/crates/oxidetalis/src/parameters/pagination.rs new file mode 100644 index 0000000..c31a912 --- /dev/null +++ b/crates/oxidetalis/src/parameters/pagination.rs @@ -0,0 +1,121 @@ +// OxideTalis Messaging Protocol homeserver implementation +// Copyright (C) 2024 OxideTalis Developers +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +//! Pagination parameters for the API + +use std::{ + fmt, + num::{NonZeroU32, NonZeroU8}, + str::FromStr, +}; + +use salvo::{ + extract::Metadata as ExtractMetadata, + oapi::{ + Components as OapiComponents, + Object, + Parameter, + ParameterIn, + Parameters, + SchemaType, + ToParameters, + }, + Extractible, + Request, +}; +use serde_json::json; + +use crate::routes::{ApiError, ApiResult}; + +#[derive(Debug)] +pub struct Pagination { + /// The page number of the result + pub page: NonZeroU32, + /// The page size + pub page_size: NonZeroU8, +} + +impl<'ex> Extractible<'ex> for Pagination { + fn metadata() -> &'ex ExtractMetadata { + static METADATA: ExtractMetadata = ExtractMetadata::new(""); + &METADATA + } + + #[allow(refining_impl_trait)] + async fn extract(req: &'ex mut Request) -> ApiResult { + let page = extract_query(req, "page", NonZeroU32::new(1).expect("is non-zero"))?; + let page_size = extract_query(req, "page_size", NonZeroU8::new(10).expect("is non-zero"))?; + + Ok(Self { page, page_size }) + } + + #[allow(refining_impl_trait)] + async fn extract_with_arg(req: &'ex mut Request, _arg: &str) -> ApiResult { + Self::extract(req).await + } +} + +impl ToParameters<'_> for Pagination { + fn to_parameters(_components: &mut OapiComponents) -> Parameters { + Parameters::new() + .parameter(create_parameter( + "page", + "Page number, starting from 1", + 1, + f64::from(u32::MAX), + )) + .parameter(create_parameter( + "page_size", + "How many items per page, starting from 1", + 10, + f64::from(u8::MAX), + )) + } +} + +/// Extract a query parameter from the request +fn extract_query(req: &Request, name: &str, default_value: T) -> ApiResult +where + ::Err: fmt::Display, +{ + Ok(req + .queries() + .get(name) + .map(|p| p.parse::()) + .transpose() + .map_err(|err| ApiError::Querys(format!("Invalid value for `{name}` query ({err})")))? + .unwrap_or(default_value)) +} + +/// Create a parameter for the pagination +fn create_parameter(name: &str, description: &str, default: usize, max: f64) -> Parameter { + Parameter::new(name) + .parameter_in(ParameterIn::Query) + .required(false) + .description(description) + .example(json!(default)) + .schema( + Object::new() + .name(name) + .description(description) + .schema_type(SchemaType::Integer) + .default_value(json!(default)) + .example(json!(default)) + .maximum(max) + .minimum(1.0) + .read_only(true), + ) +}