use rusqlite::Result as RusqliteResult;
use rusqlite::ToSql;
use rusqlite::types::FromSql;
use rusqlite::types::FromSqlResult;
use rusqlite::types::ToSqlOutput;
use rusqlite::types::ValueRef;
use serde::{Serialize,Deserialize};
use serde::Serializer;
use serde::Deserializer;
use serde::de::Error as SerdeDeError;
use url::ParseError;
pub use url::Url;
use std::error::Error;
use std::fmt::Display;
use std::fmt::Error as FmtError;
use std::fmt::Formatter;
use std::ops::Deref;
use std::str::FromStr;
#[derive(Clone,Debug,PartialEq,Eq,Hash,PartialOrd,Ord)]
pub struct UrlWithoutFragment {
url: Url,
}
impl UrlWithoutFragment {
pub fn new(mut url: Url) -> Self {
url.set_fragment(None);
Self { url }
}
pub fn take_url(self) -> Url {
self.url
}
pub fn with_fragment(mut self, fragment: Option<&str>) -> Url {
self.url.set_fragment(fragment);
self.url
}
pub fn with_fragment_string(self, fragment: Option<String>) -> Url {
if let Some(fragment) = fragment {
self.with_fragment(Some(fragment.as_str()))
} else {
self.url
}
}
}
#[derive(Debug,Clone)]
pub enum UrlWithoutFragmentParseError {
UnwantedFragment,
Url(ParseError),
}
impl Display for UrlWithoutFragmentParseError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
Self::UnwantedFragment => {
f.write_str("Fragments on URLs are explicitly disabled here.")
},
Self::Url(e) => {
e.fmt(f)
},
}
}
}
impl Error for UrlWithoutFragmentParseError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::UnwantedFragment => None,
Self::Url(e) => Some(e),
}
}
}
impl FromStr for UrlWithoutFragment {
type Err = UrlWithoutFragmentParseError;
fn from_str(input: &str) -> Result<Self, UrlWithoutFragmentParseError> {
match Url::from_str(input) {
Ok(url) => {
if url.fragment().is_some() {
return Err(UrlWithoutFragmentParseError::UnwantedFragment);
}
Ok(Self{ url })
},
Err(e) => {
Err(UrlWithoutFragmentParseError::Url(e))
}
}
}
}
impl Deref for UrlWithoutFragment {
type Target = Url;
fn deref(&self) -> &Self::Target {
&self.url
}
}
impl From<Url> for UrlWithoutFragment {
fn from(url: Url) -> Self {
Self::new(url)
}
}
impl From<UrlWithoutFragment> for Url {
fn from(url: UrlWithoutFragment) -> Self {
url.url
}
}
impl Display for UrlWithoutFragment {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
self.url.fmt(f)
}
}
impl Serialize for UrlWithoutFragment {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer
{
self.url.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for UrlWithoutFragment {
fn deserialize<D>(deserializer: D) -> Result<UrlWithoutFragment, D::Error>
where
D: Deserializer<'de>
{
match Url::deserialize(deserializer) {
Ok(url) => {
if url.fragment().is_some() {
return Err(D::Error::custom("No fragment part allowed for URLs in this place"));
}
return Ok(Self::new(url));
}
Err(e) => {
return Err(e);
}
}
}
}
impl ToSql for UrlWithoutFragment {
fn to_sql(&self) -> RusqliteResult<ToSqlOutput<'_>> {
self.url.to_sql()
}
}
impl FromSql for UrlWithoutFragment {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
Ok(Self::new(Url::column_result(value)?))
}
}