sup

A curses threads-with-tags style email client

sup.git

git clone https://supmua.dev/git/sup/

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