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

View file

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

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 client;
pub mod error;
pub mod into_url;
pub mod status;
#[cfg(feature = "hickory")]