]> git.notmuchmail.org Git - notmuch/blob - devel/schemata
emacs: Add new option notmuch-search-hide-excluded
[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; one marked with '*'
8 can repeat (with a different name). |'s indicate alternates (e.g.,
9 int|string means something can be an int or a 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 5 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 v4
34 - replace signature error integer bitmask with a set of flags for
35   individual errors.
36 - (notmuch 0.29) added message.crypto to identify overall message
37   cryptographic state
38
39 v5
40 - sorting support for notmuch show (no change to actual schema,
41   just new command line argument)
42
43 Common non-terminals
44 --------------------
45
46 # Number of seconds since the Epoch
47 unix_time = int
48
49 # Thread ID, sans "thread:"
50 threadid = string
51
52 # Message ID, sans "id:"
53 messageid = string
54
55 # E-mail header name, sans trailing colon, like "Subject" or "In-Reply-To"
56 header_name = string
57
58 notmuch show schema
59 -------------------
60
61 # A top-level set of threads (do_show)
62 # Returned by notmuch show without a --part argument
63 thread_set = [thread*]
64
65 # Top-level messages in a thread (show_messages)
66 thread = [thread_node*]
67
68 # A message and its replies (show_messages)
69 thread_node = [
70     message|null,             # null if not matched and not --entire-thread
71     [thread_node*]            # children of message
72 ]
73
74 # A message (format_part_sprinter)
75 message = {
76     # (format_message_sprinter)
77     id:             messageid,
78     match:          bool,
79     excluded:       bool,
80     filename:       [string*],
81     timestamp:      unix_time, # date header as unix time
82     date_relative:  string,   # user-friendly timestamp
83     tags:           [string*],
84
85     headers:        headers,
86     crypto:         crypto,
87     duplicate:      integer,
88     body?:          [part]    # omitted if --body=false
89 }
90
91 # when showing the message, was any or all of it decrypted?
92 msgdecstatus: "full"|"partial"
93
94 # The overall cryptographic state of the message as a whole:
95 crypto = {
96     signed?:    {
97                   status:      sigstatus,
98                   # was the set of signatures described under encrypted cover?
99                   encrypted:   bool,
100                   # which of the headers is covered by sigstatus?
101                   headers:     [header_name*]
102                 },
103     decrypted?: {
104                   status: msgdecstatus,
105                   # map encrypted headers that differed from the outside headers.
106                   # the value of each item in the map is what that field showed externally
107                   # (maybe null if it was not present in the external headers).
108                   header-mask:  { header_name*: string|null }
109                 }
110 }
111
112 # A MIME part (format_part_sprinter)
113 part = {
114     id:             int|string, # part id (currently DFS part number)
115
116     encstatus?:     encstatus,
117     sigstatus?:     sigstatus,
118
119     content-type:   string,
120     content-disposition?:       string,
121     content-id?:    string,
122     # if content-type starts with "multipart/":
123     content:        [part*],
124     # if content-type is "message/rfc822":
125     content:        [{headers: headers, body: [part]}],
126     # otherwise (leaf parts):
127     filename?:      string,
128     content-charset?: string,
129     # A leaf part's body content is optional, but may be included if
130     # it can be correctly encoded as a string.  Consumers should use
131     # this in preference to fetching the part content separately.
132     content?:       string,
133     # If a leaf part's body content is not included, the length of
134     # the encoded content (in bytes) may be given instead.
135     content-length?: int,
136     # If a leaf part's body content is not included, its transfer encoding
137     # may be given.  Using this and the encoded content length, it is
138     # possible for the consumer to estimate the decoded content length.
139     content-transfer-encoding?: string
140 }
141
142 # The headers of a message or part (format_headers_sprinter with reply = FALSE)
143 headers = {
144     Subject:        string,
145     From:           string,
146     To?:            string,
147     Cc?:            string,
148     Bcc?:           string,
149     Reply-To?:      string,
150     Date:           string,
151     extra_header_pair*
152 }
153
154 extra_header_pair=  (header_name: string)
155 # Encryption status (format_part_sprinter)
156 encstatus = [{status: "good"|"bad"}]
157
158 # Signature status (format_part_sigstatus_sprinter)
159 sigstatus = [signature*]
160
161 signature = {
162     # (signature_status_to_string)
163     status:         "good"|"bad"|"error"|"unknown",
164     # if status is "good":
165     fingerprint?:   string,
166     created?:       unix_time,
167     expires?:       unix_time,
168     userid?:        string
169     email?:         string
170     # if status is not "good":
171     keyid?:         string
172     errors?:        sig_errors
173 }
174
175 sig_errors = {
176     key-revoked?: bool,
177     key-expired?: bool,
178     sig-expired?: bool,
179     key-missing?: bool,
180     alg-unsupported?: bool,
181     crl-missing?: bool,
182     crl-too-old?: bool,
183     bad-policy?: bool,
184     sys-error?: bool,
185     tofu-conflict?: bool
186 }
187
188 notmuch search schema
189 ---------------------
190
191 # --output=summary
192 search_summary = [thread_summary*]
193
194 # --output=threads
195 search_threads = [threadid*]
196
197 # --output=messages
198 search_messages = [messageid*]
199
200 # --output=files
201 search_files = [string*]
202
203 # --output=tags
204 search_tags = [string*]
205
206 thread_summary = {
207     thread:         threadid,
208     timestamp:      unix_time,
209     date_relative:  string,   # user-friendly timestamp
210     matched:        int,      # number of matched messages
211     total:          int,      # total messages in thread
212     authors:        string,   # comma-separated names with | between
213                               # matched and unmatched
214     subject:        string,
215     tags:           [string*],
216
217     # Two stable query strings identifying exactly the matched and
218     # unmatched messages currently in this thread.  The messages
219     # matched by these queries will not change even if more messages
220     # arrive in the thread.  If there are no matched or unmatched
221     # messages, the corresponding query will be null (there is no
222     # query that matches nothing).  (Added in schema version 2.)
223     query:          [string|null, string|null],
224 }
225
226 notmuch reply schema
227 --------------------
228
229 reply = {
230     # The headers of the constructed reply
231     reply-headers: reply_headers,
232
233     # As in the show format (format_part_sprinter)
234     original: message
235 }
236
237 # Reply headers (format_headers_sprinter with reply = TRUE)
238 reply_headers = {
239     Subject:        string,
240     From:           string,
241     To?:            string,
242     Cc?:            string,
243     Bcc?:           string,
244     In-reply-to:    string,
245     References:     string
246 }