diff options
author | Ola Bini <ola.bini@gmail.com> | 2014-07-31 19:35:40 -0300 |
---|---|---|
committer | Ola Bini <ola.bini@gmail.com> | 2014-07-31 19:35:40 -0300 |
commit | e54e5ee931b3991cbb5e427e7e5d27b3f6c75e6e (patch) | |
tree | 1e0da33d22874c0ea5576818fe45958611ebda29 /fake-service/lib/smail/search.rb | |
parent | 04cf441c5ae18400c6b4865b0b37a71718dc9d46 (diff) |
Add fake-service
Diffstat (limited to 'fake-service/lib/smail/search.rb')
-rw-r--r-- | fake-service/lib/smail/search.rb | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/fake-service/lib/smail/search.rb b/fake-service/lib/smail/search.rb new file mode 100644 index 00000000..6c6b109e --- /dev/null +++ b/fake-service/lib/smail/search.rb @@ -0,0 +1,133 @@ +# Syntax notes for search: +# you can put a - in front of any search term to negate it +# you can scope a search by putting a name of a scope, a colon and then the search term WITHOUT a space. +# scoping will allow you to search for more things than otherwise available +# an unknown scope name will be assumed to be a header to search +# you can surround a search term in quotes to search for the whole thing +# multiple search terms will be ANDed together +# you can OR things by using the keyword OR/or - if you have it without parens, you will or the whole left with the whole right, until we find another or. +# if you use parenthesis, you can group together terms +# search in:_default_, in:all, in:trash, in:sent, in:drafts will only work for the WHOLE search. You can do a negation on a scoped search if it's in:trash, in:sent or in:drafts, but not for in:all + +module Smail + class Search + def initialize(q) + if q + @qtree, @search_scope = Search.compile(q) + else + @qtree, @search_scope = TrueMatch.new, Smail::MailScopeFilter::Default + end + end + + def restrict(input) + @search_scope.new(input).select do |mm| + @qtree.match?(mm) + end + end + + REGEXP_DQUOTED = /"[^"]*"/ + REGEXP_SQUOTED = /'[^']*'/ + REGEXP_SCOPE = /\w+:(".*?"|'.*?'|[^\s\)]+)/ + REGEXP_OTHER = /[^\s\)]+/ + + def self.scan_literal(qs) + if qs.check(REGEXP_DQUOTED) + StringMatch.new(qs.scan(REGEXP_DQUOTED), true) + elsif qs.check(REGEXP_SQUOTED) + StringMatch.new(qs.scan(REGEXP_SQUOTED), true) + elsif qs.check(REGEXP_OTHER) + StringMatch.new(qs.scan(REGEXP_OTHER)) + end + end + + def self.combine_search_scopes(l, r) + l + r + end + + def self.compile(q, qs = StringScanner.new(q)) + qtree = AndMatch.new + search_scope = Smail::MailScopeFilter::Default + until qs.eos? + if qs.check(/\)/) + qs.scan(/\)/) + return optimized(qtree), search_scope + end + + negated = false + if qs.check(/-/) + negated = true + qs.scan(/-/) + end + + if qs.check(/or/i) + qs.scan(/or/i) + left = qtree + qtree = OrMatch.new(left, AndMatch.new) + else + res = + if qs.check(/\(/) + qs.scan(/\(/) + v, sc = compile(q, qs) + search_scope = search_scope + sc + v + elsif qs.check(REGEXP_DQUOTED) + StringMatch.new(qs.scan(REGEXP_DQUOTED), true) + elsif qs.check(REGEXP_SQUOTED) + StringMatch.new(qs.scan(REGEXP_SQUOTED), true) + elsif qs.check(REGEXP_SCOPE) + scope = qs.scan(/\w+/) + qs.scan(/:/) + rest_node = scan_literal(qs) + v = ScopeMatch.new(scope, rest_node) + if v.is_search_scope? && !negated + search_scope = search_scope + v.search_scope + TrueMatch.new + else + v + end + elsif qs.check(REGEXP_OTHER) + StringMatch.new(qs.scan(REGEXP_OTHER)) + end + res = NegateMatch.new(res) if negated + qtree << res + end + + qs.scan(/\s+/) + end + return optimized(qtree), search_scope + end + + def self.optimized(tree) + case tree + when AndMatch + data = tree.data.reject { |d| TrueMatch === d } + if data.length == 1 + optimized(data.first) + else + AndMatch.new(data.map { |n| optimized(n)} ) + end + when OrMatch + if tree.right.is_a?(AndMatch) && tree.right.data.empty? + optimized(tree.left) + else + OrMatch.new(optimized(tree.left), optimized(tree.right)) + end + when NegateMatch + if tree.data.is_a?(NegateMatch) + optimized(tree.data.data) + else + NegateMatch.new(optimized(tree.data)) + end + else + tree + end + end + end +end + +require 'smail/search/string_match' +require 'smail/search/scope_match' +require 'smail/search/negate_match' +require 'smail/search/and_match' +require 'smail/search/or_match' +require 'smail/search/true_match' |