emacs: convert to use format-version 3
[notmuch] / devel / schemata
1 This file describes the schemata used for notmuch's structured output
2 format (currently JSON and S-Expressions).
3
4 []'s indicate lists.  List items can be marked with a '?', meaning
5 they are optional; or a '*', meaning there can be zero or more of that
6 item.  {}'s indicate an object that maps from field identifiers to
7 values.  An object field marked '?' is optional.  |'s indicate
8 alternates (e.g., int|string means something can be an int or a
9 string).
10
11 For S-Expression output, lists are printed delimited by () instead of
12 []. Objects are printed as p-lists, i.e. lists where the keys and values
13 are interleaved. Keys are printed as keywords (symbols preceded by a
14 colon), e.g. (:id "123" :time 54321 :from "foobar"). Null is printed as
15 nil, true as t and false as nil.
16
17 This is version 2 of the structured output format.
18
19 Version history
20 ---------------
21
22 v1
23 - First versioned schema release.
24 - Added part.content-length and part.content-transfer-encoding fields.
25
26 v2
27 - Added the thread_summary.query field.
28
29 v3
30 - Replaced message.filename string with a list of filenames.
31 - Added part.content-disposition field.
32
33 Common non-terminals
34 --------------------
35
36 # Number of seconds since the Epoch
37 unix_time = int
38
39 # Thread ID, sans "thread:"
40 threadid = string
41
42 # Message ID, sans "id:"
43 messageid = string
44
45 notmuch show schema
46 -------------------
47
48 # A top-level set of threads (do_show)
49 # Returned by notmuch show without a --part argument
50 thread_set = [thread*]
51
52 # Top-level messages in a thread (show_messages)
53 thread = [thread_node*]
54
55 # A message and its replies (show_messages)
56 thread_node = [
57     message|null,             # null if not matched and not --entire-thread
58     [thread_node*]            # children of message
59 ]
60
61 # A message (format_part_sprinter)
62 message = {
63     # (format_message_sprinter)
64     id:             messageid,
65     match:          bool,
66     filename:       [string*],
67     timestamp:      unix_time, # date header as unix time
68     date_relative:  string,   # user-friendly timestamp
69     tags:           [string*],
70
71     headers:        headers,
72     body?:          [part]    # omitted if --body=false
73 }
74
75 # A MIME part (format_part_sprinter)
76 part = {
77     id:             int|string, # part id (currently DFS part number)
78
79     encstatus?:     encstatus,
80     sigstatus?:     sigstatus,
81
82     content-type:   string,
83     content-disposition?:       string,
84     content-id?:    string,
85     # if content-type starts with "multipart/":
86     content:        [part*],
87     # if content-type is "message/rfc822":
88     content:        [{headers: headers, body: [part]}],
89     # otherwise (leaf parts):
90     filename?:      string,
91     content-charset?: string,
92     # A leaf part's body content is optional, but may be included if
93     # it can be correctly encoded as a string.  Consumers should use
94     # this in preference to fetching the part content separately.
95     content?:       string,
96     # If a leaf part's body content is not included, the length of
97     # the encoded content (in bytes) may be given instead.
98     content-length?: int,
99     # If a leaf part's body content is not included, its transfer encoding
100     # may be given.  Using this and the encoded content length, it is
101     # possible for the consumer to estimate the decoded content length.
102     content-transfer-encoding?: string
103 }
104
105 # The headers of a message or part (format_headers_sprinter with reply = FALSE)
106 headers = {
107     Subject:        string,
108     From:           string,
109     To?:            string,
110     Cc?:            string,
111     Bcc?:           string,
112     Reply-To?:      string,
113     Date:           string
114 }
115
116 # Encryption status (format_part_sprinter)
117 encstatus = [{status: "good"|"bad"}]
118
119 # Signature status (format_part_sigstatus_sprinter)
120 sigstatus = [signature*]
121
122 signature = {
123     # (signature_status_to_string)
124     status:         "none"|"good"|"bad"|"error"|"unknown",
125     # if status is "good":
126     fingerprint?:   string,
127     created?:       unix_time,
128     expires?:       unix_time,
129     userid?:        string
130     # if status is not "good":
131     keyid?:         string
132     # if the signature has errors:
133     errors?:        int
134 }
135
136 notmuch search schema
137 ---------------------
138
139 # --output=summary
140 search_summary = [thread_summary*]
141
142 # --output=threads
143 search_threads = [threadid*]
144
145 # --output=messages
146 search_messages = [messageid*]
147
148 # --output=files
149 search_files = [string*]
150
151 # --output=tags
152 search_tags = [string*]
153
154 thread_summary = {
155     thread:         threadid,
156     timestamp:      unix_time,
157     date_relative:  string,   # user-friendly timestamp
158     matched:        int,      # number of matched messages
159     total:          int,      # total messages in thread
160     authors:        string,   # comma-separated names with | between
161                               # matched and unmatched
162     subject:        string,
163     tags:           [string*],
164
165     # Two stable query strings identifying exactly the matched and
166     # unmatched messages currently in this thread.  The messages
167     # matched by these queries will not change even if more messages
168     # arrive in the thread.  If there are no matched or unmatched
169     # messages, the corresponding query will be null (there is no
170     # query that matches nothing).  (Added in schema version 2.)
171     query:          [string|null, string|null],
172 }
173
174 notmuch reply schema
175 --------------------
176
177 reply = {
178     # The headers of the constructed reply
179     reply-headers: reply_headers,
180
181     # As in the show format (format_part_sprinter)
182     original: message
183 }
184
185 # Reply headers (format_headers_sprinter with reply = TRUE)
186 reply_headers = {
187     Subject:        string,
188     From:           string,
189     To?:            string,
190     Cc?:            string,
191     Bcc?:           string,
192     In-reply-to:    string,
193     References:     string
194 }