lib/sup/search.rb (3247B) - raw
1 # encoding: utf-8
2
3 module Redwood
4
5 class SearchManager
6 include Redwood::Singleton
7
8 class ExpansionError < StandardError; end
9
10 attr_reader :predefined_searches
11
12 def initialize fn
13 @fn = fn
14 @searches = {}
15 if File.exist? fn
16 IO.foreach(fn) do |l|
17 l =~ /^([^:]*): (.*)$/ or raise "can't parse #{fn} line #{l.inspect}"
18 @searches[$1] = $2
19 end
20 end
21 @modified = false
22
23 @predefined_searches = { 'All mail' => 'Search all mail.' }
24 @predefined_queries = { 'All mail'.to_sym => { :qobj => Xapian::Query.new('Kmail'),
25 :load_spam => false,
26 :load_deleted => false,
27 :load_killed => false,
28 :text => 'Search all mail.'}
29 }
30 @predefined_searches.each do |k,v|
31 @searches[k] = v
32 end
33 end
34
35 def predefined_queries; return @predefined_queries; end
36 def all_searches; return @searches.keys.sort; end
37 def search_string_for name;
38 if @predefined_searches.keys.member? name
39 return name.to_sym
40 end
41 return @searches[name];
42 end
43 def valid_name? name; name =~ /^[\w-]+$/; end
44 def name_format_hint; "letters, numbers, underscores and dashes only"; end
45
46 def add name, search_string
47 return unless valid_name? name
48 if @predefined_searches.has_key? name
49 warn "cannot add search: #{name} is already taken by a predefined search"
50 return
51 end
52 @searches[name] = search_string
53 @modified = true
54 end
55
56 def rename old, new
57 return unless @searches.has_key? old
58 if [old, new].any? { |x| @predefined_searches.has_key? x }
59 warn "cannot rename search: #{old} or #{new} is already taken by a predefined search"
60 return
61 end
62 search_string = @searches[old]
63 delete old if add new, search_string
64 end
65
66 def edit name, search_string
67 return unless @searches.has_key? name
68 if @predefined_searches.has_key? name
69 warn "cannot edit predefined search: #{name}."
70 return
71 end
72 @searches[name] = search_string
73 @modified = true
74 end
75
76 def delete name
77 return unless @searches.has_key? name
78 if @predefined_searches.has_key? name
79 warn "cannot delete predefined search: #{name}."
80 return
81 end
82 @searches.delete name
83 @modified = true
84 end
85
86 def expand search_string
87 expanded = search_string.dup
88 until (matches = expanded.scan(/\{([\w-]+)\}/).flatten).empty?
89 if !(unknown = matches - @searches.keys).empty?
90 error_message = "Unknown \"#{unknown.join('", "')}\" when expanding \"#{search_string}\""
91 elsif expanded.size >= 2048
92 error_message = "Check for infinite recursion in \"#{search_string}\""
93 end
94 if error_message
95 warn error_message
96 raise ExpansionError, error_message
97 end
98 matches.each { |n| expanded.gsub! "{#{n}}", "(#{@searches[n]})" if @searches.has_key? n }
99 end
100 return expanded
101 end
102
103 def save
104 return unless @modified
105 File.open(@fn, "w:UTF-8") { |f| (@searches - @predefined_searches.keys).sort.each { |(n, s)| f.puts "#{n}: #{s}" } }
106 @modified = false
107 end
108 end
109
110 end