diff --git a/rslib/backend.proto b/rslib/backend.proto index da55c24ee..93b3376fd 100644 --- a/rslib/backend.proto +++ b/rslib/backend.proto @@ -809,9 +809,17 @@ message SearchTerm { message IdList { repeated int64 ids = 1; } + message Group { + enum Operator { + AND = 0; + OR = 1; + } + repeated SearchTerm terms = 1; + Operator op = 2; + } oneof filter { - string tag = 1; - string deck = 2; + Group group = 1; + SearchTerm negated = 2; string note = 3; uint32 template = 4; int64 nid = 5; @@ -824,8 +832,9 @@ message SearchTerm { CardState card_state = 12; IdList nids = 13; uint32 edited_in_days = 14; - SearchTerm negated = 15; + string deck = 15; int32 due_on_day = 16; + string tag = 17; } } diff --git a/rslib/src/backend/mod.rs b/rslib/src/backend/mod.rs index 95334ca76..7eae49dc3 100644 --- a/rslib/src/backend/mod.rs +++ b/rslib/src/backend/mod.rs @@ -55,6 +55,7 @@ use crate::{ }; use fluent::FluentValue; use futures::future::{AbortHandle, AbortRegistration, Abortable}; +use itertools::Itertools; use log::error; use once_cell::sync::OnceCell; use pb::{sync_status_out, BackendService}; @@ -297,6 +298,7 @@ impl From for DeckConfID { impl From for Node<'_> { fn from(msg: pb::SearchTerm) -> Self { + use pb::search_term::group::Operator; use pb::search_term::Filter; use pb::search_term::Flag; if let Some(filter) = msg.filter { @@ -359,6 +361,19 @@ impl From for Node<'_> { Flag::Blue => Node::Search(SearchNode::Flag(4)), }, Filter::Negated(term) => Node::Not(Box::new((*term).into())), + Filter::Group(group) => { + let operator = match group.op() { + Operator::And => Node::And, + Operator::Or => Node::Or, + }; + let joined = group + .terms + .into_iter() + .map(Into::into) + .intersperse(operator) + .collect(); + Node::Group(joined) + } } } else { Node::Search(SearchNode::WholeCollection) diff --git a/rslib/src/search/parser.rs b/rslib/src/search/parser.rs index ee7e2909f..33d5b6adc 100644 --- a/rslib/src/search/parser.rs +++ b/rslib/src/search/parser.rs @@ -30,7 +30,7 @@ fn parse_error(input: &str) -> nom::Err> { nom::Err::Error(ParseError::Anki(input, FailKind::Other(None))) } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum Node<'a> { And, Or,