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