diff options
Diffstat (limited to 'fake-service/lib/smail/contacts_search.rb')
-rw-r--r-- | fake-service/lib/smail/contacts_search.rb | 71 |
1 files changed, 71 insertions, 0 deletions
diff --git a/fake-service/lib/smail/contacts_search.rb b/fake-service/lib/smail/contacts_search.rb new file mode 100644 index 00000000..76ee6aa2 --- /dev/null +++ b/fake-service/lib/smail/contacts_search.rb @@ -0,0 +1,71 @@ + +# Very simple search for contacts. The search string will be something that will be prefix matched +# using a boundary before but not after. If you put in more than one word, those two will be searched +# and ANDed together. You can use double quotes or single quotes to do the obvious thing instead +module Smail + class ContactsSearch + def initialize(q) + @qtree = ContactsSearch.compile(q) + end + + def restrict(input) + input.select do |mm| + @qtree.match?(mm) + end + end + + REGEXP_DQUOTED = /"[^"]*"/ + REGEXP_SQUOTED = /'[^']*'/ + REGEXP_OTHER = /[^\s]+/ + + class AndMatch + attr_reader :data + def initialize(data = []) + @data = data + end + def <<(node) + @data << node + end + def match?(c) + self.data.all? { |mm| mm.match?(c) } + end + end + + class StringMatch + def initialize(data, quoted=false) + @data = Regexp.new(Regexp.quote(if quoted + data[1..-2] + else + data + end), Regexp::IGNORECASE) + @exact_match = /\b#{@data}/ + end + + def match_string?(str) + Array(str).any? { |ff| @exact_match.match ff } + end + + def match?(c) + match_string? ([c.name] + c.addresses.to_a).compact + end + end + + def self.compile(q) + qs = StringScanner.new(q) + qtree = AndMatch.new + until qs.eos? + res = + 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 + qtree << res + qs.scan(/\s+/) + end + qtree + end + end +end |