summaryrefslogtreecommitdiff
path: root/fake-service/lib/smail/contacts_search.rb
diff options
context:
space:
mode:
Diffstat (limited to 'fake-service/lib/smail/contacts_search.rb')
-rw-r--r--fake-service/lib/smail/contacts_search.rb71
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