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