From daef92207e962b568308198d01ba0225a7927db9 Mon Sep 17 00:00:00 2001 From: Awiteb Date: Mon, 22 Jul 2024 12:41:58 +0300 Subject: [PATCH] feat: Use `url::Host` instead of `IpOrUrl` Signed-off-by: Awiteb --- crates/oxidetalis/src/utils.rs | 6 +- crates/oxidetalis_config/src/commandline.rs | 4 +- crates/oxidetalis_config/src/defaults.rs | 6 +- crates/oxidetalis_config/src/lib.rs | 3 +- crates/oxidetalis_config/src/serde_with.rs | 49 ++++++------- crates/oxidetalis_config/src/types.rs | 77 ++++++++------------- 6 files changed, 59 insertions(+), 86 deletions(-) diff --git a/crates/oxidetalis/src/utils.rs b/crates/oxidetalis/src/utils.rs index d4b38b6..1f4ef34 100644 --- a/crates/oxidetalis/src/utils.rs +++ b/crates/oxidetalis/src/utils.rs @@ -36,11 +36,7 @@ use crate::nonce::NonceCache; pub(crate) fn postgres_url(db_config: &Postgres) -> String { format!( "postgres://{}:{}@{}:{}/{}", - db_config.user, - db_config.password, - db_config.host.as_str(), - db_config.port, - db_config.name + db_config.user, db_config.password, db_config.host, db_config.port, db_config.name ) } diff --git a/crates/oxidetalis_config/src/commandline.rs b/crates/oxidetalis_config/src/commandline.rs index c9d90c6..ac90a5c 100644 --- a/crates/oxidetalis_config/src/commandline.rs +++ b/crates/oxidetalis_config/src/commandline.rs @@ -26,7 +26,7 @@ use std::{net::IpAddr, path::PathBuf}; use clap::Parser; use oxidetalis_core::types::Size; -use crate::{types::OpenApiViewer, IpOrUrl}; +use crate::types::{Host, OpenApiViewer}; /// Header message, used in the help message const HEADER: &str = r#"Copyright (C) 2024 Awiteb , OxideTalis Contributors @@ -74,7 +74,7 @@ pub struct CliArgs { pub register_enable: Option, /// Hostname or IP address of the PostgreSQL database. #[clap(long, env = "OXIDETALIS_DB_HOST")] - pub postgres_host: Option, + pub postgres_host: Option, /// Port number of the PostgreSQL database. #[clap(long, env = "OXIDETALIS_DB_PORT")] pub postgres_port: Option, diff --git a/crates/oxidetalis_config/src/defaults.rs b/crates/oxidetalis_config/src/defaults.rs index f2c73e7..7ff91e6 100644 --- a/crates/oxidetalis_config/src/defaults.rs +++ b/crates/oxidetalis_config/src/defaults.rs @@ -81,7 +81,6 @@ pub(crate) mod openapi { /// Postgres default configs pub(crate) mod postgres { - use std::str::FromStr; pub fn user() -> String { "oxidetalis".to_owned() @@ -89,8 +88,9 @@ pub(crate) mod postgres { pub fn password() -> String { "oxidetalis".to_owned() } - pub fn host() -> crate::IpOrUrl { - crate::IpOrUrl::from_str("localhost").expect("Is a valid localhost") + pub const fn host() -> crate::Host { + #[allow(clippy::absolute_paths)] + crate::Host(url::Host::Ipv4(std::net::Ipv4Addr::new(127, 0, 0, 1))) } pub fn name() -> String { "oxidetalis_db".to_owned() diff --git a/crates/oxidetalis_config/src/lib.rs b/crates/oxidetalis_config/src/lib.rs index 90a4101..6388d57 100644 --- a/crates/oxidetalis_config/src/lib.rs +++ b/crates/oxidetalis_config/src/lib.rs @@ -95,7 +95,8 @@ pub struct Postgres { pub password: String, /// Database host #[derivative(Default(value = "defaults::postgres::host()"))] - pub host: IpOrUrl, + #[serde(with = "serde_with::host")] + pub host: Host, /// Database port #[derivative(Default(value = "defaults::postgres::port()"))] pub port: u16, diff --git a/crates/oxidetalis_config/src/serde_with.rs b/crates/oxidetalis_config/src/serde_with.rs index 61abe6a..9d58ea9 100644 --- a/crates/oxidetalis_config/src/serde_with.rs +++ b/crates/oxidetalis_config/src/serde_with.rs @@ -21,33 +21,9 @@ //! Serialize and deserialize some oxidetalis configurations -use serde::{de::Error as DeError, Deserialize, Deserializer}; +use std::str::FromStr; -/// Serialize and deserialze the string of IpOrUrl struct -pub(crate) mod ip_or_url { - use std::str::FromStr; - - use serde::{de::Error as DeError, Deserialize, Deserializer, Serializer}; - - use crate::IpOrUrl; - - pub fn serialize(value: &str, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(value) - } - - pub fn deserialize<'de, D>(de: D) -> Result - where - D: Deserializer<'de>, - { - Ok(IpOrUrl::from_str(&String::deserialize(de)?) - .map_err(DeError::custom)? - .as_str() - .to_owned()) - } -} +use serde::{de::Error as DeError, Deserialize, Deserializer, Serializer}; pub fn deserialize_url_path<'de, D>(de: D) -> Result where @@ -61,3 +37,24 @@ where } Ok(url_path) } + +pub mod host { + #[allow(clippy::wildcard_imports)] + use super::*; + use crate::Host; + + pub fn deserialize<'de, D>(de: D) -> Result + where + D: Deserializer<'de>, + { + Host::from_str(&String::deserialize(de)?) + .map_err(|_| DeError::custom("Invalid host name, invalid IPv4, IPv6 or domain name")) + } + + pub fn serialize(host: &Host, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&host.to_string()) + } +} diff --git a/crates/oxidetalis_config/src/types.rs b/crates/oxidetalis_config/src/types.rs index 1acefc1..c8449da 100644 --- a/crates/oxidetalis_config/src/types.rs +++ b/crates/oxidetalis_config/src/types.rs @@ -21,7 +21,7 @@ //! Oxidetalis config types -use std::{net::IpAddr, str::FromStr}; +use std::{fmt, str::FromStr}; use salvo_oapi::{rapidoc::RapiDoc, redoc::ReDoc, scalar::Scalar, swagger_ui::SwaggerUi}; use serde::{Deserialize, Serialize}; @@ -41,6 +41,33 @@ pub enum OpenApiViewer { SwaggerUi, } +/// Host type, a wrapper around `url::Host` +/// +/// Because `url::Host` does not implement `FromStr`, we need to wrap it +/// in a newtype to implement `FromStr` for it. +#[derive(Debug, Clone)] +pub struct Host(pub url::Host); + +impl FromStr for Host { + type Err = url::ParseError; + + fn from_str(s: &str) -> Result { + // It appears that @SimonSapin prefers not to use the `FromStr` trait. + // Instead, he is implementing `parse` without utilizing `FromStr`. + // + // - + // - + // - + Ok(Self(url::Host::parse(s)?)) + } +} + +impl fmt::Display for Host { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + impl OpenApiViewer { /// Create a router for the viewer pub fn into_router(&self, config: &crate::Config) -> salvo_core::Router { @@ -76,51 +103,3 @@ impl OpenApiViewer { } } } - -/// Type hold url or ip (used for database host) -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IpOrUrl(#[serde(with = "crate::serde_with::ip_or_url")] String); - -impl Default for IpOrUrl { - fn default() -> Self { - IpOrUrl("localhost".to_owned()) - } -} - -impl IpOrUrl { - /// Returns &str ip or url - pub fn as_str(&self) -> &str { - &self.0 - } -} - -impl FromStr for IpOrUrl { - type Err = String; - - fn from_str(s: &str) -> Result { - Ok(IpOrUrl( - if let Ok(res) = IpAddr::from_str(s).map(|i| i.to_string()) { - res - } else { - validate_domain(s)? - }, - )) - } -} - -fn validate_domain(domain: &str) -> Result { - if domain != "localhost" { - let subs = domain.split('.'); - for sub in subs { - let length = sub.chars().count(); - if !sub.chars().all(|c| c.is_alphanumeric() || c == '-') - || sub.starts_with('-') - || sub.ends_with('-') - || (length > 0 && length <= 64) - { - return Err("Invalid domain name".to_owned()); - } - } - } - Ok(domain.to_owned()) -}