lexa-framework 0.2.8

Framework web backend personnel par dessus Axum.
Documentation
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ Copyright: (c) 2023, Mike 'PhiSyX' S. (https://github.com/PhiSyX)         ┃
// ┃ SPDX-License-Identifier: MPL-2.0                                          ┃
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
// ┃                                                                           ┃
// ┃  This Source Code Form is subject to the terms of the Mozilla Public      ┃
// ┃  License, v. 2.0. If a copy of the MPL was not distributed with this      ┃
// ┃  file, You can obtain one at https://mozilla.org/MPL/2.0/.                ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

pub use tower_http::cors::*;

// --------- //
// Structure //
// --------- //

#[derive(Debug)]
#[derive(serde::Deserialize)]
pub struct Settings {
	pub preset: Option<SettingsPreset>,
	pub allow_credentials: Option<bool>,
	pub allow_headers: Option<SettingsAllowHeaders>,
	pub allow_methods: Option<SettingsAllowMethods>,
	pub allow_origin: Option<SettingsAllowOrigin>,
	pub allow_private_network: Option<bool>,
	pub expose_headers: Option<SettingsExposeHeaders>,
	pub max_age: Option<SettingsMaxAge>,
	pub vary: Option<SettingsVary>,
}

// ----------- //
// Énumération //
// ----------- //

#[derive(Debug)]
#[derive(serde::Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum SettingsPreset {
	Default,
	Permissive,
	VeryPermissive,
}

#[derive(Debug)]
#[derive(serde::Deserialize)]
#[serde(untagged)]
pub enum SettingsAllowHeaders {
	#[serde(deserialize_with = "is_wildcard")]
	Any(char),
	List(Vec<String>),
}

#[derive(Debug)]
#[derive(serde::Deserialize)]
#[serde(untagged)]
pub enum SettingsAllowMethods {
	#[serde(deserialize_with = "is_wildcard")]
	Any(char),
	Exact(crate::routing::Method),
	List(Vec<crate::routing::Method>),
}

#[derive(Debug)]
#[derive(serde::Deserialize)]
#[serde(untagged)]
pub enum SettingsAllowOrigin {
	#[serde(deserialize_with = "is_wildcard")]
	Any(char),
	Exact(String),
	List(Vec<String>),
}

#[derive(Debug)]
#[derive(serde::Deserialize)]
#[serde(untagged)]
pub enum SettingsExposeHeaders {
	#[serde(deserialize_with = "is_wildcard")]
	Any(char),
	List(Vec<String>),
}

#[derive(Debug)]
#[derive(serde::Deserialize)]
#[serde(untagged)]
pub enum SettingsMaxAge {
	Exact(u64),
}

#[derive(Debug)]
#[derive(serde::Deserialize)]
#[serde(untagged)]
pub enum SettingsVary {
	List(Vec<String>),
}

// -------------- //
// Implémentation // -> Interface
// -------------- //

impl From<&SettingsAllowHeaders> for AllowHeaders {
	fn from(this: &SettingsAllowHeaders) -> Self {
		match this {
			| SettingsAllowHeaders::Any(_) => Self::any(),
			| SettingsAllowHeaders::List(list) => {
				Self::list(list.iter().flat_map(|hr| hr.parse()))
			}
		}
	}
}

impl From<&SettingsAllowMethods> for AllowMethods {
	fn from(this: &SettingsAllowMethods) -> Self {
		match this {
			| SettingsAllowMethods::Any(_) => Self::any(),
			| SettingsAllowMethods::Exact(method) => {
				Self::exact(match method {
					| crate::routing::Method::DELETE => {
						axum::http::Method::DELETE
					}
					| crate::routing::Method::GET => axum::http::Method::GET,
					| crate::routing::Method::HEAD => axum::http::Method::HEAD,
					| crate::routing::Method::OPTIONS => {
						axum::http::Method::OPTIONS
					}
					| crate::routing::Method::PATCH => {
						axum::http::Method::PATCH
					}
					| crate::routing::Method::POST => axum::http::Method::POST,
					| crate::routing::Method::PUT => axum::http::Method::PUT,
					| crate::routing::Method::TRACE => {
						axum::http::Method::TRACE
					}
				})
			}
			| SettingsAllowMethods::List(list) => {
				Self::list(list.iter().map(|method| {
					match method {
						| crate::routing::Method::DELETE => {
							axum::http::Method::DELETE
						}
						| crate::routing::Method::GET => {
							axum::http::Method::GET
						}
						| crate::routing::Method::HEAD => {
							axum::http::Method::HEAD
						}
						| crate::routing::Method::OPTIONS => {
							axum::http::Method::OPTIONS
						}
						| crate::routing::Method::PATCH => {
							axum::http::Method::PATCH
						}
						| crate::routing::Method::POST => {
							axum::http::Method::POST
						}
						| crate::routing::Method::PUT => {
							axum::http::Method::PUT
						}
						| crate::routing::Method::TRACE => {
							axum::http::Method::TRACE
						}
					}
				}))
			}
		}
	}
}

impl From<&SettingsAllowOrigin> for AllowOrigin {
	fn from(this: &SettingsAllowOrigin) -> Self {
		match this {
			| SettingsAllowOrigin::Any(_) => Self::any(),
			| SettingsAllowOrigin::Exact(value) => {
				if let Ok(value) = value.parse() {
					return Self::exact(value);
				}
				Self::default()
			}
			| SettingsAllowOrigin::List(list) => {
				Self::list(list.iter().flat_map(|value| value.parse()))
			}
		}
	}
}

impl From<&SettingsExposeHeaders> for ExposeHeaders {
	fn from(this: &SettingsExposeHeaders) -> Self {
		match this {
			| SettingsExposeHeaders::Any(_) => Self::any(),
			| SettingsExposeHeaders::List(list) => {
				Self::list(list.iter().flat_map(|value| value.parse()))
			}
		}
	}
}

impl From<&SettingsMaxAge> for MaxAge {
	fn from(this: &SettingsMaxAge) -> Self {
		match this {
			| SettingsMaxAge::Exact(secs) => {
				Self::exact(std::time::Duration::from_secs(*secs))
			}
		}
	}
}

impl From<&SettingsVary> for Vary {
	fn from(this: &SettingsVary) -> Self {
		match this {
			| SettingsVary::List(list) => {
				Self::list(list.iter().flat_map(|value| value.parse()))
			}
		}
	}
}

// -------- //
// Fonction //
// -------- //

fn is_wildcard<'de, D>(deserializer: D) -> Result<char, D::Error>
where
	D: serde::Deserializer<'de>,
{
	let value = <_ as serde::Deserialize>::deserialize(deserializer)?;
	if value != '*' {
		return Err(serde::de::Error::custom(
			"Seul le caractère '*' est autorisé.",
		));
	}
	Ok(value)
}