From 2554e80ce870f64024c86e58cbf92e73e965d3a7 Mon Sep 17 00:00:00 2001 From: RumovZ Date: Wed, 14 Apr 2021 09:53:45 +0200 Subject: [PATCH] Drop ANDs and optional quotes when normalising --- rslib/src/search/writer.rs | 110 +++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 54 deletions(-) diff --git a/rslib/src/search/writer.rs b/rslib/src/search/writer.rs index ecfd9e722..f96823830 100644 --- a/rslib/src/search/writer.rs +++ b/rslib/src/search/writer.rs @@ -68,7 +68,7 @@ pub fn write_nodes(nodes: &[Node]) -> String { fn write_node(node: &Node) -> String { use Node::*; match node { - And => " AND ".to_string(), + And => " ".to_string(), Or => " OR ".to_string(), Not(n) => format!("-{}", write_node(n)), Group(ns) => format!("({})", write_nodes(ns)), @@ -79,33 +79,37 @@ fn write_node(node: &Node) -> String { fn write_search_node(node: &SearchNode) -> String { use SearchNode::*; match node { - UnqualifiedText(s) => quote(&s.replace(":", "\\:")), + UnqualifiedText(s) => maybe_quote(&s.replace(":", "\\:")), SingleField { field, text, is_re } => write_single_field(field, text, *is_re), - AddedInDays(u) => format!("\"added:{}\"", u), - EditedInDays(u) => format!("\"edited:{}\"", u), + AddedInDays(u) => format!("added:{}", u), + EditedInDays(u) => format!("edited:{}", u), CardTemplate(t) => write_template(t), - Deck(s) => quote(&format!("deck:{}", s)), - DeckId(DeckIdType(i)) => format!("\"did:{}\"", i), - NotetypeId(NotetypeIdType(i)) => format!("\"mid:{}\"", i), - Notetype(s) => quote(&format!("note:{}", s)), + Deck(s) => maybe_quote(&format!("deck:{}", s)), + DeckId(DeckIdType(i)) => format!("did:{}", i), + NotetypeId(NotetypeIdType(i)) => format!("mid:{}", i), + Notetype(s) => maybe_quote(&format!("note:{}", s)), Rated { days, ease } => write_rated(days, ease), - Tag(s) => quote(&format!("tag:{}", s)), + Tag(s) => maybe_quote(&format!("tag:{}", s)), Duplicates { notetype_id, text } => write_dupe(notetype_id, text), State(k) => write_state(k), - Flag(u) => format!("\"flag:{}\"", u), - NoteIds(s) => format!("\"nid:{}\"", s), - CardIds(s) => format!("\"cid:{}\"", s), + Flag(u) => format!("flag:{}", u), + NoteIds(s) => format!("nid:{}", s), + CardIds(s) => format!("cid:{}", s), Property { operator, kind } => write_property(operator, kind), - WholeCollection => "\"deck:*\"".to_string(), - Regex(s) => quote(&format!("re:{}", s)), - NoCombining(s) => quote(&format!("nc:{}", s)), - WordBoundary(s) => quote(&format!("w:{}", s)), + WholeCollection => "deck:*".to_string(), + Regex(s) => maybe_quote(&format!("re:{}", s)), + NoCombining(s) => maybe_quote(&format!("nc:{}", s)), + WordBoundary(s) => maybe_quote(&format!("w:{}", s)), } } -/// Escape and wrap in double quotes. -fn quote(txt: &str) -> String { - format!("\"{}\"", txt.replace("\"", "\\\"")) +/// Escape double quotes and wrap in double quotes if necessary. +fn maybe_quote(txt: &str) -> String { + if txt.chars().any(|c| " \u{3000}()".contains(c)) { + format!("\"{}\"", txt.replace("\"", "\\\"")) + } else { + txt.replace("\"", "\\\"") + } } fn write_single_field(field: &str, text: &str, is_re: bool) -> String { @@ -115,35 +119,35 @@ fn write_single_field(field: &str, text: &str, is_re: bool) -> String { } else { text.to_string() }; - quote(&format!("{}:{}{}", field.replace(":", "\\:"), re, &text)) + maybe_quote(&format!("{}:{}{}", field.replace(":", "\\:"), re, &text)) } fn write_template(template: &TemplateKind) -> String { match template { - TemplateKind::Ordinal(u) => format!("\"card:{}\"", u + 1), - TemplateKind::Name(s) => format!("\"card:{}\"", s), + TemplateKind::Ordinal(u) => format!("card:{}", u + 1), + TemplateKind::Name(s) => maybe_quote(&format!("card:{}", s)), } } fn write_rated(days: &u32, ease: &RatingKind) -> String { use RatingKind::*; match ease { - AnswerButton(n) => format!("\"rated:{}:{}\"", days, n), - AnyAnswerButton => format!("\"rated:{}\"", days), - ManualReschedule => format!("\"resched:{}\"", days), + AnswerButton(n) => format!("rated:{}:{}", days, n), + AnyAnswerButton => format!("rated:{}", days), + ManualReschedule => format!("resched:{}", days), } } /// Escape double quotes and backslashes: \" fn write_dupe(notetype_id: &NotetypeId, text: &str) -> String { let esc = text.replace(r"\", r"\\").replace('"', r#"\""#); - format!("\"dupe:{},{}\"", notetype_id, esc) + maybe_quote(&format!("dupe:{},{}", notetype_id, esc)) } fn write_state(kind: &StateKind) -> String { use StateKind::*; format!( - "\"is:{}\"", + "is:{}", match kind { New => "new", Review => "review", @@ -160,16 +164,16 @@ fn write_state(kind: &StateKind) -> String { fn write_property(operator: &str, kind: &PropertyKind) -> String { use PropertyKind::*; match kind { - Due(i) => format!("\"prop:due{}{}\"", operator, i), - Interval(u) => format!("\"prop:ivl{}{}\"", operator, u), - Reps(u) => format!("\"prop:reps{}{}\"", operator, u), - Lapses(u) => format!("\"prop:lapses{}{}\"", operator, u), - Ease(f) => format!("\"prop:ease{}{}\"", operator, f), - Position(u) => format!("\"prop:pos{}{}\"", operator, u), + Due(i) => format!("prop:due{}{}", operator, i), + Interval(u) => format!("prop:ivl{}{}", operator, u), + Reps(u) => format!("prop:reps{}{}", operator, u), + Lapses(u) => format!("prop:lapses{}{}", operator, u), + Ease(f) => format!("prop:ease{}{}", operator, f), + Position(u) => format!("prop:pos{}{}", operator, u), Rated(u, ease) => match ease { - RatingKind::AnswerButton(val) => format!("\"prop:rated{}{}:{}\"", operator, u, val), - RatingKind::AnyAnswerButton => format!("\"prop:rated{}{}\"", operator, u), - RatingKind::ManualReschedule => format!("\"prop:resched{}{}\"", operator, u), + RatingKind::AnswerButton(val) => format!("prop:rated{}{}:{}", operator, u, val), + RatingKind::AnyAnswerButton => format!("prop:rated{}{}", operator, u), + RatingKind::ManualReschedule => format!("prop:resched{}{}", operator, u), }, } } @@ -192,17 +196,15 @@ mod test { #[test] fn normalizing() { - assert_eq!(r#""(" AND "-""#, normalize_search(r"\( \-").unwrap()); - assert_eq!(r#""deck::""#, normalize_search(r"deck:\:").unwrap()); - assert_eq!(r#""\*" OR "\:""#, normalize_search(r"\* or \:").unwrap()); assert_eq!( - r#""field:foo""#, - normalize_search(r#"field:"foo""#).unwrap() - ); - assert_eq!( - r#""prop:ease>1""#, - normalize_search("prop:ease>1.0").unwrap() + r#"foo "b a r""#, + normalize_search(r#""foo" "b a r""#).unwrap() ); + assert_eq!(r#""(" -"#, normalize_search(r"\( \-").unwrap()); + assert_eq!("deck::", normalize_search(r"deck:\:").unwrap()); + assert_eq!(r"\* OR \:", normalize_search(r"\* or \:").unwrap()); + assert_eq!("field:foo", normalize_search(r#"field:"foo""#).unwrap()); + assert_eq!("prop:ease>1", normalize_search("prop:ease>1.0").unwrap()); } #[test] @@ -213,7 +215,7 @@ mod test { vec![Node::Search(SearchNode::UnqualifiedText("foo".to_string()))], Node::Search(SearchNode::UnqualifiedText("bar".to_string())) ), - r#""foo" AND "bar""#, + "foo bar", ); assert_eq!( concatenate_searches( @@ -221,7 +223,7 @@ mod test { vec![Node::Search(SearchNode::UnqualifiedText("foo".to_string()))], Node::Search(SearchNode::UnqualifiedText("bar".to_string())) ), - r#""foo" OR "bar""#, + "foo OR bar", ); assert_eq!( concatenate_searches( @@ -229,7 +231,7 @@ mod test { vec![Node::Search(SearchNode::WholeCollection)], Node::Search(SearchNode::UnqualifiedText("bar".to_string())) ), - r#""deck:*" OR "bar""#, + "deck:* OR bar", ); assert_eq!( concatenate_searches( @@ -237,7 +239,7 @@ mod test { vec![], Node::Search(SearchNode::UnqualifiedText("bar".to_string())) ), - r#""bar""#, + "bar", ); } @@ -245,29 +247,29 @@ mod test { fn replacing() -> Result<()> { assert_eq!( replace_search_node(parse("deck:baz bar")?, parse("deck:foo")?.pop().unwrap()), - r#""deck:foo" AND "bar""#, + "deck:foo bar", ); assert_eq!( replace_search_node( parse("tag:foo Or tag:bar")?, parse("tag:baz")?.pop().unwrap() ), - r#""tag:baz" OR "tag:baz""#, + "tag:baz OR tag:baz", ); assert_eq!( replace_search_node( parse("foo or (-foo tag:baz)")?, parse("bar")?.pop().unwrap() ), - r#""bar" OR (-"bar" AND "tag:baz")"#, + "bar OR (-bar tag:baz)", ); assert_eq!( replace_search_node(parse("is:due")?, parse("-is:new")?.pop().unwrap()), - r#""is:due""# + "is:due" ); assert_eq!( replace_search_node(parse("added:1")?, parse("is:due")?.pop().unwrap()), - r#""added:1""# + "added:1" ); Ok(())