bayard 0.9.0

A distributed search server.
pub mod all;
pub mod boolean;
pub mod boost;
pub mod fuzzy_term;
pub mod phrase;
pub mod query_string;
pub mod range;
pub mod regex;
pub mod term;

use std::fmt;

use tantivy::{
    query::{Query, QueryClone},
    Searcher,
};

use crate::proto::index::{query::Kind as ProtoQueryKind, Query as ProtoQuery};

use self::{
    all::create_all_query,
    boolean::{create_boolean_query, BooleanQueryArgs},
    boost::{create_boost_query, BoostQueryArgs},
    fuzzy_term::{create_fuzzy_term_query, FuzzyTermQueryArgs},
    phrase::{create_phrase_query, PhraseQueryArgs},
    query_string::{create_query_string_query, QueryStringQueryArgs},
    range::{create_range_query, RangeQueryArgs},
    regex::{create_regex_query, RegexQueryArgs},
    term::{create_term_query, TermQueryArgs},
};

#[derive(Debug, Clone, Copy)]
pub enum QueryFactoryErrorKind {
    CreateError,
    InvalidArgument,
}

impl QueryFactoryErrorKind {
    pub fn with_error<E>(self, source: E) -> QueryFactoryError
    where
        anyhow::Error: From<E>,
    {
        QueryFactoryError {
            kind: self,
            source: From::from(source),
        }
    }
}

#[derive(thiserror::Error, Debug)]
#[error("QueryFactoryError(kind={kind:?}, source={source})")]
pub struct QueryFactoryError {
    pub kind: QueryFactoryErrorKind,
    #[source]
    source: anyhow::Error,
}

impl QueryFactoryError {
    pub fn add_context<C>(self, ctx: C) -> Self
    where
        C: fmt::Display + Send + Sync + 'static,
    {
        QueryFactoryError {
            kind: self.kind,
            source: self.source.context(ctx),
        }
    }

    pub fn kind(&self) -> QueryFactoryErrorKind {
        self.kind
    }
}

pub fn create_query(
    query: &ProtoQuery,
    searcher: &Searcher,
) -> Result<Box<dyn Query>, QueryFactoryError> {
    let kind = ProtoQueryKind::from_i32(query.kind).ok_or_else(|| {
        QueryFactoryErrorKind::InvalidArgument
            .with_error(anyhow::anyhow!("Undefined query kind {}", query.kind))
    })?;

    let query_obj = match kind {
        ProtoQueryKind::All => create_all_query().box_clone(),
        ProtoQueryKind::Boolean => {
            let args = serde_json::from_slice::<BooleanQueryArgs>(query.options.as_slice())
                .map_err(|e| QueryFactoryErrorKind::InvalidArgument.with_error(e))?;
            create_boolean_query(&args, searcher)?.box_clone()
        }
        ProtoQueryKind::Boost => {
            let args = serde_json::from_slice::<BoostQueryArgs>(query.options.as_slice())
                .map_err(|e| QueryFactoryErrorKind::InvalidArgument.with_error(e))?;
            create_boost_query(&args, searcher)?.box_clone()
        }
        ProtoQueryKind::FuzzyTerm => {
            let args = serde_json::from_slice::<FuzzyTermQueryArgs>(query.options.as_slice())
                .map_err(|e| QueryFactoryErrorKind::InvalidArgument.with_error(e))?;
            create_fuzzy_term_query(&args, searcher)?.box_clone()
        }
        ProtoQueryKind::Phrase => {
            let args = serde_json::from_slice::<PhraseQueryArgs>(query.options.as_slice())
                .map_err(|e| QueryFactoryErrorKind::InvalidArgument.with_error(e))?;
            create_phrase_query(&args, searcher)?.box_clone()
        }
        ProtoQueryKind::QueryString => {
            let args = serde_json::from_slice::<QueryStringQueryArgs>(query.options.as_slice())
                .map_err(|e| QueryFactoryErrorKind::InvalidArgument.with_error(e))?;
            create_query_string_query(&args, searcher)?
        }
        ProtoQueryKind::Range => {
            let args = serde_json::from_slice::<RangeQueryArgs>(query.options.as_slice())
                .map_err(|e| QueryFactoryErrorKind::InvalidArgument.with_error(e))?;
            create_range_query(&args, searcher)?.box_clone()
        }
        ProtoQueryKind::Regex => {
            let args = serde_json::from_slice::<RegexQueryArgs>(query.options.as_slice())
                .map_err(|e| QueryFactoryErrorKind::InvalidArgument.with_error(e))?;
            create_regex_query(&args, searcher)?.box_clone()
        }
        ProtoQueryKind::Term => {
            let args = serde_json::from_slice::<TermQueryArgs>(query.options.as_slice())
                .map_err(|e| QueryFactoryErrorKind::InvalidArgument.with_error(e))?;
            create_term_query(&args, searcher)?.box_clone()
        }
        _ => {
            return Err(QueryFactoryErrorKind::InvalidArgument
                .with_error(anyhow::anyhow!("Unsupported query kind {:?}", kind)))
        }
    };

    Ok(query_obj)
}