]> git.cworth.org Git - sup/blob - lib/sup/source.rb
labels now fully determined by sources.yaml, and lots of improvements to sup-config
[sup] / lib / sup / source.rb
1 module Redwood
2
3 class SourceError < StandardError; end
4 class OutOfSyncSourceError < SourceError; end
5 class FatalSourceError < SourceError; end
6
7 class Source
8   ## Implementing a new source should be easy, because Sup only needs
9   ## to be able to:
10   ##  1. See how many messages it contains
11   ##  2. Get an arbitrary message
12   ##  3. (optional) see whether the source has marked it read or not
13   ##
14   ## In particular, Sup doesn't need to move messages, mark them as
15   ## read, delete them, or anything else. (Well, it's nice to be able
16   ## to delete them, but that is optional.)
17   ##
18   ## On the other hand, Sup assumes that you can assign each message a
19   ## unique integer id, such that newer messages have higher ids than
20   ## earlier ones, and that those ids stay constant across sessions
21   ## (in the absence of some other client going in and fucking
22   ## everything up). For example, for mboxes I use the file offset of
23   ## the start of the message. If a source does NOT have that
24   ## capability, e.g. IMAP, then you have to do a little more work to
25   ## simulate it.
26   ##
27   ## To write a new source, subclass this class, and implement:
28   ##
29   ## - start_offset
30   ## - end_offset (exclusive!)
31   ## - load_header offset
32   ## - load_message offset
33   ## - raw_header offset
34   ## - raw_full_message offset
35   ## - check
36   ## - next (or each, if you prefer): should return a message and an
37   ##   array of labels.
38   ##
39   ## ... where "offset" really means unique id. (You can tell I
40   ## started with mbox.)
41   ##
42   ## All exceptions relating to accessing the source must be caught
43   ## and rethrown as FatalSourceErrors or OutOfSyncSourceErrors.
44   ## OutOfSyncSourceErrors should be used for problems that a call to
45   ## sup-sync will fix (namely someone's been playing with the source
46   ## from another client); FatalSourceErrors can be used for anything
47   ## else (e.g. the imap server is down or the maildir is missing.)
48   ##
49   ## Finally, be sure the source is thread-safe, since it WILL be
50   ## pummelled from multiple threads at once.
51   ##
52   ## Examples for you to look at: mbox/loader.rb, imap.rb, and
53   ## maildir.rb.
54
55   ## let's begin!
56   ##
57   ## dirty? means cur_offset has changed, so the source info needs to
58   ## be re-saved to sources.yaml.
59   bool_reader :usual, :archived, :dirty
60   attr_reader :uri, :cur_offset
61   attr_accessor :id
62
63   def initialize uri, initial_offset=nil, usual=true, archived=false, id=nil
64     raise ArgumentError, "id must be an integer: #{id.inspect}" unless id.is_a? Fixnum if id
65
66     @uri = uri
67     @cur_offset = initial_offset
68     @usual = usual
69     @archived = archived
70     @id = id
71     @dirty = false
72   end
73
74   def to_s; @uri.to_s; end
75   def seek_to! o; self.cur_offset = o; end
76   def reset!; seek_to! start_offset; end
77   def == o; o.uri == uri; end
78   def done?; (self.cur_offset ||= start_offset) >= end_offset; end
79   def is_source_for? uri; URI(self.uri) == URI(uri); end
80
81   ## check should throw a FatalSourceError or an OutOfSyncSourcError
82   ## if it can detect a problem. it is called when the sup starts up
83   ## to proactively notify the user of any source problems.
84   def check; end
85
86   def each
87     self.cur_offset ||= start_offset
88     until done?
89       n, labels = self.next
90       raise "no message" unless n
91       yield n, labels
92     end
93   end
94
95 protected
96   
97   def cur_offset= o
98     @cur_offset = o
99     @dirty = true
100   end
101 end
102
103 end