feat: take impl IntoUrl instead of only &str

This commit is contained in:
DarkCat09 2024-08-30 11:02:45 +04:00
parent 3f2b0b5fd2
commit 8628b78598
Signed by: DarkCat09
GPG key ID: BD3CE9B65916CD82
4 changed files with 57 additions and 17 deletions

View file

@ -21,7 +21,7 @@ async fn main() -> Result<(), LibError> {
let config = parse_args(); let config = parse_args();
let client = build_client(&config).await?; let client = build_client(&config).await?;
let mut resp = client.request(&config.url).await?; let mut resp = client.request(config.url).await?;
{ {
let status_code = resp.status().status_code(); let status_code = resp.status().status_code();

View file

@ -18,6 +18,7 @@ use std::net::SocketAddr;
use crate::{ use crate::{
certs::{SelfsignedCertVerifier, ServerName}, certs::{SelfsignedCertVerifier, ServerName},
error::*, error::*,
into_url::IntoUrl,
status::*, status::*,
}; };
use builder::ClientBuilder; use builder::ClientBuilder;
@ -29,7 +30,6 @@ use tokio::{
net::TcpStream, net::TcpStream,
}; };
use tokio_rustls::{client::TlsStream, rustls, TlsConnector}; use tokio_rustls::{client::TlsStream, rustls, TlsConnector};
use url::Url;
pub type ThisResponse = Response<BufReader<TlsStream<TcpStream>>>; pub type ThisResponse = Response<BufReader<TlsStream<TcpStream>>>;
@ -50,7 +50,7 @@ impl Client {
impl Client { impl Client {
/// Perform a Gemini request with the specified URL. /// Perform a Gemini request with the specified URL.
/// Host and port (1965 by default) are parsed from `url_str` /// Host and port (1965 by default) are parsed from `url`
/// after scheme and userinfo checks. /// after scheme and userinfo checks.
/// On success, [`Response`] is returned. /// On success, [`Response`] is returned.
/// ///
@ -65,9 +65,9 @@ impl Client {
/// ///
/// # Errors /// # Errors
/// - See [`Client::request_with_no_redirect`]. /// - See [`Client::request_with_no_redirect`].
pub async fn request(&self, url_str: &str) -> Result<ThisResponse, LibError> { pub async fn request(&self, url: impl IntoUrl) -> Result<ThisResponse, LibError> {
// first request // first request
let mut resp = self.request_with_no_redirect(url_str).await?; let mut resp = self.request_with_no_redirect(url).await?;
let mut i: u8 = 0; let mut i: u8 = 0;
const MAX: u8 = 5; const MAX: u8 = 5;
@ -86,7 +86,7 @@ impl Client {
/// Perform a Gemini request with the specified URL /// Perform a Gemini request with the specified URL
/// **without** following redirections. /// **without** following redirections.
/// Host and port (1965 by default) are parsed from `url_str` /// Host and port (1965 by default) are parsed from `url`
/// after scheme and userinfo checks. /// after scheme and userinfo checks.
/// On success, [`Response`] is returned. /// On success, [`Response`] is returned.
/// ///
@ -97,21 +97,16 @@ impl Client {
/// - [`InvalidUrl::UserinfoPresent`] is returned when the given URL contains /// - [`InvalidUrl::UserinfoPresent`] is returned when the given URL contains
/// a userinfo portion (`user:password@`) -- it is forbidden by the Gemini specification. /// a userinfo portion (`user:password@`) -- it is forbidden by the Gemini specification.
/// - See [`Client::request_with_host`] for the rest. /// - See [`Client::request_with_host`] for the rest.
pub async fn request_with_no_redirect(&self, url_str: &str) -> Result<ThisResponse, LibError> { pub async fn request_with_no_redirect(
let url = Url::parse(url_str).map_err(InvalidUrl::ParseError)?; &self,
// deny non-Gemini requests url: impl IntoUrl,
if url.scheme() != "gemini" { ) -> Result<ThisResponse, LibError> {
return Err(InvalidUrl::SchemeNotGemini.into()); let url = url.into_url()?;
}
// userinfo (user:pswd@) is not allowed in Gemini
if !url.username().is_empty() {
return Err(InvalidUrl::UserinfoPresent.into());
}
let host = url.host_str().ok_or(InvalidUrl::ConvertError)?; let host = url.host_str().ok_or(InvalidUrl::ConvertError)?;
let port = url.port().unwrap_or(1965); let port = url.port().unwrap_or(1965);
self.request_with_host(url_str, host, port).await self.request_with_host(url.as_str(), host, port).await
} }
/// Perform a Gemini request with the specified host, port and URL. /// Perform a Gemini request with the specified host, port and URL.

44
src/into_url.rs Normal file
View file

@ -0,0 +1,44 @@
use url::Url;
use crate::{InvalidUrl, LibError};
pub trait IntoUrl {
fn into_url(self) -> Result<Url, LibError>;
}
impl IntoUrl for Url {
fn into_url(self) -> Result<Url, LibError> {
// deny non-Gemini requests
if self.scheme() != "gemini" {
return Err(InvalidUrl::SchemeNotGemini.into());
}
// userinfo (user:pswd@) is not allowed in Gemini
if !self.username().is_empty() {
return Err(InvalidUrl::UserinfoPresent.into());
}
Ok(self)
}
}
impl IntoUrl for &str {
#[inline]
fn into_url(self) -> Result<Url, LibError> {
Url::parse(self)?.into_url()
}
}
impl IntoUrl for &String {
#[inline]
fn into_url(self) -> Result<Url, LibError> {
Url::parse(self)?.into_url()
}
}
impl IntoUrl for String {
#[inline]
fn into_url(self) -> Result<Url, LibError> {
Url::parse(&self)?.into_url()
}
}

View file

@ -13,6 +13,7 @@
pub mod certs; pub mod certs;
pub mod client; pub mod client;
pub mod error; pub mod error;
pub mod into_url;
pub mod status; pub mod status;
#[cfg(feature = "hickory")] #[cfg(feature = "hickory")]