feat: Use url::Host
instead of IpOrUrl
Signed-off-by: Awiteb <a@4rs.nl>
This commit is contained in:
parent
1c273d746b
commit
daef92207e
6 changed files with 59 additions and 86 deletions
|
@ -36,11 +36,7 @@ use crate::nonce::NonceCache;
|
||||||
pub(crate) fn postgres_url(db_config: &Postgres) -> String {
|
pub(crate) fn postgres_url(db_config: &Postgres) -> String {
|
||||||
format!(
|
format!(
|
||||||
"postgres://{}:{}@{}:{}/{}",
|
"postgres://{}:{}@{}:{}/{}",
|
||||||
db_config.user,
|
db_config.user, db_config.password, db_config.host, db_config.port, db_config.name
|
||||||
db_config.password,
|
|
||||||
db_config.host.as_str(),
|
|
||||||
db_config.port,
|
|
||||||
db_config.name
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ use std::{net::IpAddr, path::PathBuf};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use oxidetalis_core::types::Size;
|
use oxidetalis_core::types::Size;
|
||||||
|
|
||||||
use crate::{types::OpenApiViewer, IpOrUrl};
|
use crate::types::{Host, OpenApiViewer};
|
||||||
|
|
||||||
/// Header message, used in the help message
|
/// Header message, used in the help message
|
||||||
const HEADER: &str = r#"Copyright (C) 2024 Awiteb <a@4rs.nl>, OxideTalis Contributors
|
const HEADER: &str = r#"Copyright (C) 2024 Awiteb <a@4rs.nl>, OxideTalis Contributors
|
||||||
|
@ -74,7 +74,7 @@ pub struct CliArgs {
|
||||||
pub register_enable: Option<bool>,
|
pub register_enable: Option<bool>,
|
||||||
/// Hostname or IP address of the PostgreSQL database.
|
/// Hostname or IP address of the PostgreSQL database.
|
||||||
#[clap(long, env = "OXIDETALIS_DB_HOST")]
|
#[clap(long, env = "OXIDETALIS_DB_HOST")]
|
||||||
pub postgres_host: Option<IpOrUrl>,
|
pub postgres_host: Option<Host>,
|
||||||
/// Port number of the PostgreSQL database.
|
/// Port number of the PostgreSQL database.
|
||||||
#[clap(long, env = "OXIDETALIS_DB_PORT")]
|
#[clap(long, env = "OXIDETALIS_DB_PORT")]
|
||||||
pub postgres_port: Option<u16>,
|
pub postgres_port: Option<u16>,
|
||||||
|
|
|
@ -81,7 +81,6 @@ pub(crate) mod openapi {
|
||||||
|
|
||||||
/// Postgres default configs
|
/// Postgres default configs
|
||||||
pub(crate) mod postgres {
|
pub(crate) mod postgres {
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
pub fn user() -> String {
|
pub fn user() -> String {
|
||||||
"oxidetalis".to_owned()
|
"oxidetalis".to_owned()
|
||||||
|
@ -89,8 +88,9 @@ pub(crate) mod postgres {
|
||||||
pub fn password() -> String {
|
pub fn password() -> String {
|
||||||
"oxidetalis".to_owned()
|
"oxidetalis".to_owned()
|
||||||
}
|
}
|
||||||
pub fn host() -> crate::IpOrUrl {
|
pub const fn host() -> crate::Host {
|
||||||
crate::IpOrUrl::from_str("localhost").expect("Is a valid localhost")
|
#[allow(clippy::absolute_paths)]
|
||||||
|
crate::Host(url::Host::Ipv4(std::net::Ipv4Addr::new(127, 0, 0, 1)))
|
||||||
}
|
}
|
||||||
pub fn name() -> String {
|
pub fn name() -> String {
|
||||||
"oxidetalis_db".to_owned()
|
"oxidetalis_db".to_owned()
|
||||||
|
|
|
@ -95,7 +95,8 @@ pub struct Postgres {
|
||||||
pub password: String,
|
pub password: String,
|
||||||
/// Database host
|
/// Database host
|
||||||
#[derivative(Default(value = "defaults::postgres::host()"))]
|
#[derivative(Default(value = "defaults::postgres::host()"))]
|
||||||
pub host: IpOrUrl,
|
#[serde(with = "serde_with::host")]
|
||||||
|
pub host: Host,
|
||||||
/// Database port
|
/// Database port
|
||||||
#[derivative(Default(value = "defaults::postgres::port()"))]
|
#[derivative(Default(value = "defaults::postgres::port()"))]
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
|
|
|
@ -21,33 +21,9 @@
|
||||||
|
|
||||||
//! Serialize and deserialize some oxidetalis configurations
|
//! 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
|
use serde::{de::Error as DeError, Deserialize, Deserializer, Serializer};
|
||||||
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<S>(value: &str, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
serializer.serialize_str(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deserialize<'de, D>(de: D) -> Result<String, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
Ok(IpOrUrl::from_str(&String::deserialize(de)?)
|
|
||||||
.map_err(DeError::custom)?
|
|
||||||
.as_str()
|
|
||||||
.to_owned())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deserialize_url_path<'de, D>(de: D) -> Result<String, D::Error>
|
pub fn deserialize_url_path<'de, D>(de: D) -> Result<String, D::Error>
|
||||||
where
|
where
|
||||||
|
@ -61,3 +37,24 @@ where
|
||||||
}
|
}
|
||||||
Ok(url_path)
|
Ok(url_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod host {
|
||||||
|
#[allow(clippy::wildcard_imports)]
|
||||||
|
use super::*;
|
||||||
|
use crate::Host;
|
||||||
|
|
||||||
|
pub fn deserialize<'de, D>(de: D) -> Result<Host, D::Error>
|
||||||
|
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<S>(host: &Host, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(&host.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
//! Oxidetalis config types
|
//! 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 salvo_oapi::{rapidoc::RapiDoc, redoc::ReDoc, scalar::Scalar, swagger_ui::SwaggerUi};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -41,6 +41,33 @@ pub enum OpenApiViewer {
|
||||||
SwaggerUi,
|
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<Self, Self::Err> {
|
||||||
|
// It appears that @SimonSapin prefers not to use the `FromStr` trait.
|
||||||
|
// Instead, he is implementing `parse` without utilizing `FromStr`.
|
||||||
|
//
|
||||||
|
// - <https://github.com/servo/rust-url/pull/18#issuecomment-53467026>
|
||||||
|
// - <https://github.com/servo/rust-url/pull/107#issuecomment-100611345>
|
||||||
|
// - <https://github.com/servo/rust-url/issues/286#issuecomment-284193315>
|
||||||
|
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 {
|
impl OpenApiViewer {
|
||||||
/// Create a router for the viewer
|
/// Create a router for the viewer
|
||||||
pub fn into_router(&self, config: &crate::Config) -> salvo_core::Router {
|
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<Self, Self::Err> {
|
|
||||||
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<String, String> {
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue