Split files

This commit is contained in:
Wojciech Kozlowski 2024-08-29 22:34:24 +02:00
parent a236052b4b
commit f4f9b0bf3c
2 changed files with 177 additions and 170 deletions

View File

@ -1,7 +1,9 @@
//! Module for interacting with the [MusicBrainz API](https://musicbrainz.org/doc/MusicBrainz_API).
mod query;
use std::fmt;
pub use query::{Expression, Query};
use serde::Deserialize;
use url::form_urlencoded;
@ -31,138 +33,6 @@ impl<Http: IMusicBrainzHttp> MusicBrainzClient<Http> {
}
}
pub enum Logical {
Unary(Unary),
Binary(Boolean),
}
impl fmt::Display for Logical {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Logical::Unary(u) => write!(f, "{u}"),
Logical::Binary(b) => write!(f, "{b}"),
}
}
}
pub enum Unary {
Require,
Prohibit,
}
impl fmt::Display for Unary {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Unary::Require => write!(f, "+"),
Unary::Prohibit => write!(f, "-"),
}
}
}
pub enum Boolean {
And,
Or,
Not,
}
impl fmt::Display for Boolean {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Boolean::And => write!(f, "AND "),
Boolean::Or => write!(f, "OR "),
Boolean::Not => write!(f, "NOT "),
}
}
}
pub enum Expression<Entity> {
Term(Entity),
Expr(Query<Entity>),
}
impl<Entity: fmt::Display> fmt::Display for Expression<Entity> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Expression::Term(t) => write!(f, "{t}"),
Expression::Expr(q) => write!(f, "({q})"),
}
}
}
pub struct Query<Entity> {
left: (Option<Unary>, Box<Expression<Entity>>),
right: Vec<(Logical, Box<Expression<Entity>>)>,
}
impl<Entity: fmt::Display> fmt::Display for Query<Entity> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(u) = &self.left.0 {
write!(f, "{u}")?;
}
write!(f, "{}", self.left.1)?;
for (logical, expr) in self.right.iter() {
write!(f, " {logical}{expr}")?;
}
Ok(())
}
}
impl<'a, Entity> Query<Entity> {
pub fn expression(expr: Expression<Entity>) -> Self {
Query {
left: (None, Box::new(expr)),
right: vec![],
}
}
pub fn require(expr: Expression<Entity>) -> Self {
Query {
left: (Some(Unary::Require), Box::new(expr)),
right: vec![],
}
}
pub fn and_require(mut self, expr: Expression<Entity>) -> Self {
self.right
.push((Logical::Unary(Unary::Require), Box::new(expr)));
self
}
pub fn prohibit(expr: Expression<Entity>) -> Self {
Query {
left: (Some(Unary::Prohibit), Box::new(expr)),
right: vec![],
}
}
pub fn and_prohibit(mut self, expr: Expression<Entity>) -> Self {
self.right
.push((Logical::Unary(Unary::Prohibit), Box::new(expr)));
self
}
pub fn and(mut self, expr: Expression<Entity>) -> Self {
self.right
.push((Logical::Binary(Boolean::And), Box::new(expr)));
self
}
pub fn or(mut self, expr: Expression<Entity>) -> Self {
self.right
.push((Logical::Binary(Boolean::Or), Box::new(expr)));
self
}
pub fn not(mut self, expr: Expression<Entity>) -> Self {
self.right
.push((Logical::Binary(Boolean::Not), Box::new(expr)));
self
}
}
pub enum SearchReleaseGroup<'a> {
NoField(&'a str),
Arid(&'a Mbid),
@ -276,44 +146,6 @@ mod tests {
use super::*;
#[test]
fn lucene_logical() {
let query = Query::expression(Expression::no_field("jakarta apache"))
.or(Expression::no_field("jakarta"));
assert_eq!(format!("{query}"), "\"jakarta apache\" OR \"jakarta\"");
let query = Query::expression(Expression::no_field("jakarta apache"))
.and(Expression::no_field("jakarta"));
assert_eq!(format!("{query}"), "\"jakarta apache\" AND \"jakarta\"");
let query =
Query::require(Expression::no_field("jakarta")).or(Expression::no_field("lucene"));
assert_eq!(format!("{query}"), "+\"jakarta\" OR \"lucene\"");
let query = Query::expression(Expression::no_field("jakarta apache"))
.not(Expression::no_field("Apache Lucene"));
assert_eq!(
format!("{query}"),
"\"jakarta apache\" NOT \"Apache Lucene\""
);
let query = Query::expression(Expression::no_field("jakarta apache"))
.and_prohibit(Expression::no_field("Apache Lucene"));
assert_eq!(format!("{query}"), "\"jakarta apache\" -\"Apache Lucene\"");
}
#[test]
fn lucene_grouping() {
let query = Query::expression(Expression::Expr(
Query::expression(Expression::no_field("jakarta")).or(Expression::no_field("apache")),
))
.and(Expression::no_field("website"));
assert_eq!(
format!("{query}"),
"(\"jakarta\" OR \"apache\") AND \"website\""
);
}
#[test]
fn search_release_group() {
let mut http = MockIMusicBrainzHttp::new();

View File

@ -0,0 +1,175 @@
use std::fmt;
pub enum Logical {
Unary(Unary),
Binary(Boolean),
}
impl fmt::Display for Logical {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Logical::Unary(u) => write!(f, "{u}"),
Logical::Binary(b) => write!(f, "{b}"),
}
}
}
pub enum Unary {
Require,
Prohibit,
}
impl fmt::Display for Unary {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Unary::Require => write!(f, "+"),
Unary::Prohibit => write!(f, "-"),
}
}
}
pub enum Boolean {
And,
Or,
Not,
}
impl fmt::Display for Boolean {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Boolean::And => write!(f, "AND "),
Boolean::Or => write!(f, "OR "),
Boolean::Not => write!(f, "NOT "),
}
}
}
pub enum Expression<Entity> {
Term(Entity),
Expr(Query<Entity>),
}
impl<Entity: fmt::Display> fmt::Display for Expression<Entity> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Expression::Term(t) => write!(f, "{t}"),
Expression::Expr(q) => write!(f, "({q})"),
}
}
}
pub struct Query<Entity> {
left: (Option<Unary>, Box<Expression<Entity>>),
right: Vec<(Logical, Box<Expression<Entity>>)>,
}
impl<Entity: fmt::Display> fmt::Display for Query<Entity> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(u) = &self.left.0 {
write!(f, "{u}")?;
}
write!(f, "{}", self.left.1)?;
for (logical, expr) in self.right.iter() {
write!(f, " {logical}{expr}")?;
}
Ok(())
}
}
impl<'a, Entity> Query<Entity> {
pub fn expression(expr: Expression<Entity>) -> Self {
Query {
left: (None, Box::new(expr)),
right: vec![],
}
}
pub fn require(expr: Expression<Entity>) -> Self {
Query {
left: (Some(Unary::Require), Box::new(expr)),
right: vec![],
}
}
pub fn and_require(mut self, expr: Expression<Entity>) -> Self {
self.right
.push((Logical::Unary(Unary::Require), Box::new(expr)));
self
}
pub fn prohibit(expr: Expression<Entity>) -> Self {
Query {
left: (Some(Unary::Prohibit), Box::new(expr)),
right: vec![],
}
}
pub fn and_prohibit(mut self, expr: Expression<Entity>) -> Self {
self.right
.push((Logical::Unary(Unary::Prohibit), Box::new(expr)));
self
}
pub fn and(mut self, expr: Expression<Entity>) -> Self {
self.right
.push((Logical::Binary(Boolean::And), Box::new(expr)));
self
}
pub fn or(mut self, expr: Expression<Entity>) -> Self {
self.right
.push((Logical::Binary(Boolean::Or), Box::new(expr)));
self
}
pub fn not(mut self, expr: Expression<Entity>) -> Self {
self.right
.push((Logical::Binary(Boolean::Not), Box::new(expr)));
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lucene_logical() {
let query = Query::expression(Expression::no_field("jakarta apache"))
.or(Expression::no_field("jakarta"));
assert_eq!(format!("{query}"), "\"jakarta apache\" OR \"jakarta\"");
let query = Query::expression(Expression::no_field("jakarta apache"))
.and(Expression::no_field("jakarta"));
assert_eq!(format!("{query}"), "\"jakarta apache\" AND \"jakarta\"");
let query =
Query::require(Expression::no_field("jakarta")).or(Expression::no_field("lucene"));
assert_eq!(format!("{query}"), "+\"jakarta\" OR \"lucene\"");
let query = Query::expression(Expression::no_field("jakarta apache"))
.not(Expression::no_field("Apache Lucene"));
assert_eq!(
format!("{query}"),
"\"jakarta apache\" NOT \"Apache Lucene\""
);
let query = Query::expression(Expression::no_field("jakarta apache"))
.and_prohibit(Expression::no_field("Apache Lucene"));
assert_eq!(format!("{query}"), "\"jakarta apache\" -\"Apache Lucene\"");
}
#[test]
fn lucene_grouping() {
let query = Query::expression(Expression::Expr(
Query::expression(Expression::no_field("jakarta")).or(Expression::no_field("apache")),
))
.and(Expression::no_field("website"));
assert_eq!(
format!("{query}"),
"(\"jakarta\" OR \"apache\") AND \"website\""
);
}
}