bin/sup-sync in sup-0.11 vs bin/sup-sync in sup-0.12

- old
+ new

@@ -1,11 +1,11 @@ #!/usr/bin/env ruby require 'uri' require 'rubygems' require 'trollop' -require "sup"; Redwood::check_library_version_against "0.11" +require "sup"; Redwood::check_library_version_against "0.12" PROGRESS_UPDATE_INTERVAL = 15 # seconds class Float def to_s; sprintf '%.2f', self; end @@ -51,20 +51,10 @@ where <source>* is zero or more source URIs. If no sources are given, sync from all usual sources. Supported source URI schemes can be seen by running "sup-add --help". -Options controlling WHICH messages sup-sync operates on: -EOS - opt :new, "Operate on new messages only. Don't scan over the entire source. (Default.)", :short => :none - opt :changed, "Scan over the entire source for messages that have been deleted, altered, or moved from another source." - opt :restored, "Operate only on those messages included in a dump file as specified by --restore which have changed state." - opt :all, "Operate on all messages in the source, regardless of newness or changedness." - opt :start_at, "For --changed, --restored and --all, start at a particular offset.", :type => :int - -text <<EOS - Options controlling HOW message state is altered: EOS opt :asis, "If the message is already in the index, preserve its state. Otherwise, use default source state. (Default.)", :short => :none opt :restore, "Restore message state from a dump file created with sup-dump. If a message is not in this dumpfile, act as --asis.", :type => String, :short => :none opt :discard, "Discard any message state in the index and use the default source state. Dangerous!", :short => :none @@ -80,20 +70,13 @@ opt :optimize, "As the final operation, optimize the index." opt :all_sources, "Scan over all sources.", :short => :none opt :dry_run, "Don't actually modify the index. Probably only useful with --verbose.", :short => "-n" opt :version, "Show version information", :short => :none - conflicts :changed, :all, :new, :restored conflicts :asis, :restore, :discard end -Trollop::die :restored, "requires --restore" if opts[:restored] unless opts[:restore] -if opts[:start_at] - Trollop::die :start_at, "must be non-negative" if opts[:start_at] < 0 - Trollop::die :start_at, "requires either --changed, --restored or --all" unless opts[:changed] || opts[:restored] || opts[:all] -end -target = [:new, :changed, :all, :restored].find { |x| opts[x] } || :new op = [:asis, :restore, :discard].find { |x| opts[x] } || :asis Redwood::start index = Redwood::Index.init @@ -114,145 +97,97 @@ seen = {} index.lock_interactively or exit begin index.load + if(s = Redwood::SourceManager.source_for Redwood::SentManager.source_uri) + Redwood::SentManager.source = s + else + Redwood::SourceManager.add_source Redwood::SentManager.default_source + end + sources = if opts[:all_sources] Redwood::SourceManager.sources elsif ARGV.empty? Redwood::SourceManager.usual_sources else ARGV.map do |uri| Redwood::SourceManager.source_for uri or Trollop::die "Unknown source: #{uri}. Did you add it with sup-add first?" end end - ## for all target specifications except for only-new messages, reset the - ## source to the beginning (or to the user-specified starting point.) - unless target == :new - if opts[:start_at] - Trollop::die :start_at, "can only be used on one source" unless sources.size == 1 - sources.first.seek_to! opts[:start_at] - sources.first.correct_offset! if sources.first.respond_to?(:correct_offset!) - else - sources.each { |s| s.reset! } - end - end - sources.each do |source| puts "Scanning #{source}..." num_added = num_updated = num_scanned = num_restored = 0 last_info_time = start_time = Time.now - Redwood::PollManager.each_message_from source do |m| - num_scanned += 1 - seen[m.id] = true - old_m = index.build_message m.id + Redwood::PollManager.poll_from source do |action,m,old_m,progress| + if action == :delete + puts "Deleting #{m.id}" + elsif action == :add + num_scanned += 1 + seen[m.id] = true - case target - when :changed - ## skip this message if we're operating only on changed messages, the - ## message is in the index, and it's unchanged from what the source is - ## reporting. - next if old_m && old_m.source.id == m.source.id && old_m.source_info == m.source_info - when :restored - ## skip if we're operating on restored messages, and this one - ## ain't (or we wouldn't be making a change) - next unless old_m && restored_state[m.id] && restored_state[m.id] != old_m.labels - when :new - ## nothing to do; we'll consider all messages starting at the start offset, which - ## hasn't been changed. - when :all - ## nothing to do; we'll consider all messages starting at the start offset, which - ## was reset to the beginning above. - end + ## tweak source labels according to commandline arguments if necessary + m.labels.delete :inbox if opts[:archive] + m.labels.delete :unread if opts[:read] + m.labels += opts[:extra_labels].to_set_of_symbols(",") - ## tweak source labels according to commandline arguments if necessary - m.labels.delete :inbox if opts[:archive] - m.labels.delete :unread if opts[:read] - m.labels += opts[:extra_labels].to_set_of_symbols(",") - - ## decide what to do based on message labels and the operation we're performing - dothis, new_labels = case - when (op == :restore) && restored_state[m.id] - if old_m && (old_m.labels != restored_state[m.id]) - num_restored += 1 - [:update_message_state, restored_state[m.id]] - elsif old_m.nil? - num_restored += 1 - m.labels = restored_state[m.id] - :add_message + ## decide what to do based on message labels and the operation we're performing + dothis = case + when (op == :restore) && restored_state[m.id] + if old_m && (old_m.labels != restored_state[m.id]) + num_restored += 1 + m.labels = restored_state[m.id] + :update_message_state + elsif old_m.nil? + num_restored += 1 + m.labels = restored_state[m.id] + :add_message + else + # labels are the same; don't do anything + end + when op == :discard + if old_m && (old_m.labels != m.labels) + :update_message_state + else + # labels are the same; don't do anything + end else - # labels are the same; don't do anything + if old_m + :update_message + else + :add_message + end end - when op == :discard - if old_m && (old_m.labels != m.labels) - [:update_message_state, m.labels] - else - # labels are the same; don't do anything + + ## now, actually do the operation + case dothis + when :add_message + puts "Adding new message #{source}##{m.source_info} with labels #{m.labels}" if opts[:verbose] + num_added += 1 + when :update_message + puts "Updating message #{source}##{m.source_info}; labels #{old_m.labels} => #{m.labels}; offset #{old_m.source_info} => #{m.source_info}" if opts[:verbose] + num_updated += 1 + when :update_message_state + puts "Changing flags for #{source}##{m.source_info} from #{old_m.labels} to #{m.labels}" if opts[:verbose] + num_updated += 1 end - else - ## duplicate behavior of poll mode: if index_state is non-nil, this is a newer - ## version of an older message, so merge in any new labels except :unread and - ## :inbox. - ## - ## TODO: refactor such that this isn't duplicated - if old_m - m.labels = old_m.labels + (m.labels - [:unread, :inbox]) - :update_message - else - :add_message + + if Time.now - last_info_time > PROGRESS_UPDATE_INTERVAL + last_info_time = Time.now + elapsed = last_info_time - start_time + pctdone = progress * 100.0 + remaining = (100.0 - pctdone) * (elapsed.to_f / pctdone) + printf "## read %dm (~%.0f%%) @ %.1fm/s. %s elapsed, ~%s remaining\n", num_scanned, pctdone, num_scanned / elapsed, elapsed.to_time_s, remaining.to_time_s end + else fail end - - ## now, actually do the operation - case dothis - when :add_message - puts "Adding new message #{source}##{m.source_info} with labels #{m.labels}" if opts[:verbose] - index.add_message m unless opts[:dry_run] - num_added += 1 - when :update_message - puts "Updating message #{source}##{m.source_info}; labels #{old_m.labels} => #{m.labels}; offset #{old_m.source_info} => #{m.source_info}" if opts[:verbose] - index.update_message m unless opts[:dry_run] - num_updated += 1 - when :update_message_state - puts "Changing flags for #{source}##{m.source_info} from #{m.labels} to #{new_labels}" if opts[:verbose] - m.labels = new_labels - index.update_message_state m unless opts[:dry_run] - num_updated += 1 - end - - if Time.now - last_info_time > PROGRESS_UPDATE_INTERVAL - last_info_time = Time.now - elapsed = last_info_time - start_time - pctdone = source.respond_to?(:pct_done) ? source.pct_done : 100.0 * (source.cur_offset.to_f - source.start_offset).to_f / (source.end_offset - source.start_offset).to_f - remaining = (100.0 - pctdone) * (elapsed.to_f / pctdone) - printf "## read %dm (~%.0f%%) @ %.1fm/s. %s elapsed, ~%s remaining, offset #{source.cur_offset}\n", num_scanned, pctdone, num_scanned / elapsed, elapsed.to_time_s, remaining.to_time_s - end + next if opts[:dry_run] end puts "Scanned #{num_scanned}, added #{num_added}, updated #{num_updated} messages from #{source}." puts "Restored state on #{num_restored} (#{100.0 * num_restored / num_scanned}%) messages." if num_restored > 0 - end - - ## delete any messages in the index that claim they're from one of - ## these sources, but that we didn't see. - if (target == :all || target == :changed) - puts "Deleting missing messages from the index..." - num_del, num_scanned = 0, 0 - sources.each do |source| - raise "no source id for #{source}" unless source.id - index.each_message :source_id => source.id, :load_spam => true, :load_deleted => true, :load_killed => true do |m| - num_scanned += 1 - unless seen[m.id] - next unless m.source_info >= opts[:start_at] if opts[:start_at] - puts "Deleting #{m.id}" if opts[:verbose] - index.delete m.id unless opts[:dry_run] - num_del += 1 - end - end - end - puts "Deleted #{num_del} / #{num_scanned} messages" end index.save if opts[:optimize]