use serde::{Serialize,Deserialize};
use url::Url;
use criterium::DirectMatch;
use criterium::NumberCriterium;
use criterium::rusqlite::assembler::*;
use criterium::StringCriterium;
use criterium::sql::Field;
use crate::criterium::OriginCriterium;
use crate::database::fields::UrlField;
use crate::database::fields::OriginField;
use crate::Origin;
use crate::url::UrlWithoutFragment;
#[derive(Clone,Debug,Serialize,Deserialize)]
#[serde(rename_all="snake_case")]
pub enum UrlCriterium {
User(StringCriterium),
Password(StringCriterium),
Path(StringCriterium),
Query(StringCriterium),
Fragment(StringCriterium),
Scheme(StringCriterium),
Host(StringCriterium),
Port(NumberCriterium<u16>),
OriginEquals(Origin),
Equals(Url),
SameDocumentAs(UrlWithoutFragment),
}
impl UrlCriterium {
pub fn assemble_rusqlite_query_with_fragment<F: Field + From<UrlField> + From<OriginField>>(
&self,
assembly_context: &AssemblyContext,
fragment_assembly_context: &AssemblyContext,
fragment_field: &F,
) -> InvertableRusqliteQuery<F> {
match self {
Self::Fragment(c) => c.assemble_query(fragment_assembly_context, fragment_field),
Self::Equals(url) => {
let fq = Into::<StringCriterium>::into(url.fragment())
.assemble_query(&fragment_assembly_context.in_and_block(), fragment_field)
.get_corrected_query();
Self::SameDocumentAs(url.clone().into())
.assemble_finished_rusqlite_query(&assembly_context.in_and_block(), &())
.and(fq)
.parenthesise_where_clause()
.as_invertable()
},
_ => self.assemble_rusqlite_query(assembly_context, &()),
}
}
}
impl<F: Field + From<UrlField> + From<OriginField>> AssembleRusqliteQuery<F, ()> for UrlCriterium {
fn assemble_rusqlite_query(
&self,
assembly_context: &AssemblyContext,
_context: &(),
) -> InvertableRusqliteQuery<F> {
let mut join_origin = false;
let mut query = match self {
Self::User(c) => c.assemble_query(assembly_context, &UrlField::Username.into()),
Self::Password(c) => c.assemble_query(assembly_context, &UrlField::Password.into()),
Self::Path(c) => c.assemble_query(assembly_context, &UrlField::Path.into()),
Self::Query(c) => c.assemble_query(assembly_context, &UrlField::Query.into()),
Self::Fragment(c) => match c {
StringCriterium::IsNone =>
RusqliteQuery::static_bool(true),
_ =>
RusqliteQuery::static_bool(false),
}.as_invertable(),
Self::Scheme(c) => {
join_origin = true;
c.assemble_query(&assembly_context.prefix_with("url_"), &OriginField::Scheme.into())
},
Self::Host(c) => {
join_origin = true;
c.assemble_query(&assembly_context.prefix_with("url_"), &OriginField::Host.into())
},
Self::Port(c) => {
join_origin = true;
c.assemble_query(&assembly_context.prefix_with("url_"), &OriginField::Port.into())
},
Self::OriginEquals(origin) => {
join_origin = true;
OriginCriterium::Equals(origin.clone()).assemble_rusqlite_query(&assembly_context.prefix_with("url_"), &())
},
Self::Equals(url) => {
if url.fragment().is_none() {
StringCriterium::Equals(url.to_string())
.assemble_query(assembly_context, &UrlField::StrUrl.into())
} else {
RusqliteQuery::static_bool(false).as_invertable()
}
},
Self::SameDocumentAs(url) => {
StringCriterium::Equals(url.to_string())
.assemble_query(assembly_context, &UrlField::StrUrl.into())
}
};
if join_origin {
query = query.inner_join(
None,
OriginField::OriginId.into(),
Some(assembly_context.prefix()),
UrlField::OriginId.into(),
);
}
return query;
}
}
impl DirectMatch<Url> for UrlCriterium {
type Output = bool;
fn criterium_match(&self, data: &Url) -> bool {
match self {
Self::User(c) => c.criterium_match(data.username()),
Self::Password(c) => c.criterium_match(&data.password()),
Self::Path(c) => c.criterium_match(data.path()),
Self::Query(c) => c.criterium_match(&data.query()),
Self::Fragment(c) => c.criterium_match(&data.fragment()),
Self::Scheme(c) => c.criterium_match(&Some(data.scheme())),
Self::Host(c) => c.criterium_match(&data.host_str()),
Self::Port(c) => c.criterium_match(&data.port_or_known_default()),
Self::OriginEquals(origin) =>
origin.host.as_deref() == data.host_str() &&
origin.port == data.port_or_known_default() &&
origin.scheme == data.scheme(),
Self::Equals(url) => url == data,
Self::SameDocumentAs(url) =>
*url == UrlWithoutFragment::new(data.clone()),
}
}
}
impl From<OriginCriterium> for UrlCriterium {
fn from(origin_criterium: OriginCriterium) -> Self {
match origin_criterium {
OriginCriterium::Scheme(c) => Self::Scheme(c),
OriginCriterium::Host(c) => Self::Host(c),
OriginCriterium::Port(c) => Self::Port(c),
OriginCriterium::Equals(origin) => Self::OriginEquals(origin),
}
}
}