Provide filter searches through backend
This commit is contained in:
parent
fe4da25e15
commit
9ef691c06f
@ -84,6 +84,7 @@ service BackendService {
|
||||
|
||||
// searching
|
||||
|
||||
rpc FilterToSearch (FilterToSearchIn) returns (String);
|
||||
rpc NormalizeSearch (String) returns (String);
|
||||
rpc SearchCards (SearchCardsIn) returns (SearchCardsOut);
|
||||
rpc SearchNotes (SearchNotesIn) returns (SearchNotesOut);
|
||||
@ -756,6 +757,35 @@ message BuiltinSearchOrder {
|
||||
bool reverse = 2;
|
||||
}
|
||||
|
||||
message FilterToSearchIn {
|
||||
enum NamedFilter {
|
||||
WHOLE_COLLECTION = 0;
|
||||
CURRENT_DECK = 1;
|
||||
ADDED_TODAY = 2;
|
||||
STUDIED_TODAY = 3;
|
||||
AGAIN_TODAY = 4;
|
||||
NEW = 5;
|
||||
LEARN = 6;
|
||||
REVIEW = 7;
|
||||
DUE = 8;
|
||||
SUSPENDED = 9;
|
||||
BURIED = 10;
|
||||
RED_FLAG = 11;
|
||||
ORANGE_FLAG = 12;
|
||||
GREEN_FLAG = 13;
|
||||
BLUE_FLAG = 14;
|
||||
NO_FLAG = 15;
|
||||
ANY_FLAG = 16;
|
||||
}
|
||||
oneof filter {
|
||||
NamedFilter name = 1;
|
||||
string tag = 2;
|
||||
string deck = 3;
|
||||
string note = 4;
|
||||
uint32 template = 5;
|
||||
}
|
||||
}
|
||||
|
||||
message ConcatenateSearchesIn {
|
||||
enum Separator {
|
||||
AND = 0;
|
||||
|
@ -36,8 +36,8 @@ use crate::{
|
||||
sched::new::NewCardSortOrder,
|
||||
sched::timespan::{answer_button_time, time_span},
|
||||
search::{
|
||||
concatenate_searches, negate_search, normalize_search, replace_search_term, BoolSeparator,
|
||||
SortMode,
|
||||
concatenate_searches, negate_search, normalize_search, replace_search_term, write_nodes,
|
||||
BoolSeparator, Node, SearchNode, SortMode, StateKind, TemplateKind,
|
||||
},
|
||||
stats::studied_today,
|
||||
sync::{
|
||||
@ -45,7 +45,7 @@ use crate::{
|
||||
SyncActionRequired, SyncAuth, SyncMeta, SyncOutput, SyncStage,
|
||||
},
|
||||
template::RenderedNode,
|
||||
text::{extract_av_tags, strip_av_tags, AVTag},
|
||||
text::{escape_anki_wildcards, extract_av_tags, strip_av_tags, AVTag},
|
||||
timestamp::TimestampSecs,
|
||||
types::Usn,
|
||||
};
|
||||
@ -277,6 +277,46 @@ impl From<pb::DeckConfigId> for DeckConfID {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<pb::FilterToSearchIn> for Node<'_> {
|
||||
fn from(msg: pb::FilterToSearchIn) -> Self {
|
||||
use pb::filter_to_search_in::Filter as F;
|
||||
use pb::filter_to_search_in::NamedFilter as NF;
|
||||
use Node as N;
|
||||
use SearchNode as SN;
|
||||
match msg.filter.unwrap_or(F::Name(NF::WholeCollection as i32)) {
|
||||
F::Name(name) => match NF::from_i32(name).unwrap_or(NF::WholeCollection) {
|
||||
NF::WholeCollection => N::Search(SN::WholeCollection),
|
||||
NF::CurrentDeck => N::Search(SN::Deck("current".into())),
|
||||
NF::AddedToday => N::Search(SN::AddedInDays(1)),
|
||||
NF::StudiedToday => N::Search(SN::Rated {
|
||||
days: 1,
|
||||
ease: None,
|
||||
}),
|
||||
NF::AgainToday => N::Search(SN::Rated {
|
||||
days: 1,
|
||||
ease: Some(1),
|
||||
}),
|
||||
NF::New => N::Search(SN::State(StateKind::New)),
|
||||
NF::Learn => N::Search(SN::State(StateKind::Learning)),
|
||||
NF::Review => N::Search(SN::State(StateKind::Review)),
|
||||
NF::Due => N::Search(SN::State(StateKind::Due)),
|
||||
NF::Suspended => N::Search(SN::State(StateKind::Suspended)),
|
||||
NF::Buried => N::Search(SN::State(StateKind::Buried)),
|
||||
NF::RedFlag => N::Search(SN::Flag(1)),
|
||||
NF::OrangeFlag => N::Search(SN::Flag(2)),
|
||||
NF::GreenFlag => N::Search(SN::Flag(3)),
|
||||
NF::BlueFlag => N::Search(SN::Flag(4)),
|
||||
NF::NoFlag => N::Search(SN::Flag(0)),
|
||||
NF::AnyFlag => N::Not(Box::new(N::Search(SN::Flag(0)))),
|
||||
},
|
||||
F::Tag(s) => N::Search(SN::Tag(escape_anki_wildcards(&s).into_owned().into())),
|
||||
F::Deck(s) => N::Search(SN::Deck(escape_anki_wildcards(&s).into_owned().into())),
|
||||
F::Note(s) => N::Search(SN::NoteType(escape_anki_wildcards(&s).into_owned().into())),
|
||||
F::Template(u) => N::Search(SN::CardTemplate(TemplateKind::Ordinal(u as u16))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BoolSeparatorProto> for BoolSeparator {
|
||||
fn from(sep: BoolSeparatorProto) -> Self {
|
||||
match sep {
|
||||
@ -408,6 +448,10 @@ impl BackendService for Backend {
|
||||
// searching
|
||||
//-----------------------------------------------
|
||||
|
||||
fn filter_to_search(&self, input: pb::FilterToSearchIn) -> Result<pb::String> {
|
||||
Ok(write_nodes(&[input.into()]).into())
|
||||
}
|
||||
|
||||
fn normalize_search(&self, input: pb::String) -> Result<pb::String> {
|
||||
Ok(normalize_search(&input.val)?.into())
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ mod sqlwriter;
|
||||
mod writer;
|
||||
|
||||
pub use cards::SortMode;
|
||||
pub use parser::{Node, PropertyKind, SearchNode, StateKind, TemplateKind};
|
||||
pub use writer::{
|
||||
concatenate_searches, negate_search, normalize_search, replace_search_term, BoolSeparator,
|
||||
concatenate_searches, negate_search, normalize_search, replace_search_term, write_nodes,
|
||||
BoolSeparator,
|
||||
};
|
||||
|
@ -42,7 +42,7 @@ impl<I> From<nom::Err<(I, ErrorKind)>> for ParseError {
|
||||
type ParseResult<T> = std::result::Result<T, ParseError>;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(super) enum Node<'a> {
|
||||
pub enum Node<'a> {
|
||||
And,
|
||||
Or,
|
||||
Not(Box<Node<'a>>),
|
||||
@ -51,7 +51,7 @@ pub(super) enum Node<'a> {
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub(super) enum SearchNode<'a> {
|
||||
pub enum SearchNode<'a> {
|
||||
// text without a colon
|
||||
UnqualifiedText(Cow<'a, str>),
|
||||
// foo:bar, where foo doesn't match a term below
|
||||
@ -91,7 +91,7 @@ pub(super) enum SearchNode<'a> {
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub(super) enum PropertyKind {
|
||||
pub enum PropertyKind {
|
||||
Due(i32),
|
||||
Interval(u32),
|
||||
Reps(u32),
|
||||
@ -101,7 +101,7 @@ pub(super) enum PropertyKind {
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub(super) enum StateKind {
|
||||
pub enum StateKind {
|
||||
New,
|
||||
Review,
|
||||
Learning,
|
||||
@ -113,7 +113,7 @@ pub(super) enum StateKind {
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub(super) enum TemplateKind<'a> {
|
||||
pub enum TemplateKind<'a> {
|
||||
Ordinal(u16),
|
||||
Name(Cow<'a, str>),
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ pub fn replace_search_term(search: &str, replacement: &str) -> Result<String> {
|
||||
Ok(write_nodes(&nodes))
|
||||
}
|
||||
|
||||
fn write_nodes<'a, I>(nodes: I) -> String
|
||||
pub fn write_nodes<'a, I>(nodes: I) -> String
|
||||
where
|
||||
I: IntoIterator<Item = &'a Node<'a>>,
|
||||
{
|
||||
|
@ -331,6 +331,14 @@ pub(crate) fn escape_sql(txt: &str) -> Cow<str> {
|
||||
RE.replace_all(&txt, r"\$0")
|
||||
}
|
||||
|
||||
/// Escape Anki wildcards and the backslash for escaping them: \*_
|
||||
pub(crate) fn escape_anki_wildcards(txt: &str) -> Cow<str> {
|
||||
lazy_static! {
|
||||
static ref RE: Regex = Regex::new(r"[\\*_]").unwrap();
|
||||
}
|
||||
RE.replace_all(&txt, r"\$0")
|
||||
}
|
||||
|
||||
/// Compare text with a possible glob, folding case.
|
||||
pub(crate) fn matches_glob(text: &str, search: &str) -> bool {
|
||||
if is_glob(search) {
|
||||
|
Loading…
Reference in New Issue
Block a user