1 Return-Path: <tomi.ollila@iki.fi>
\r
2 X-Original-To: notmuch@notmuchmail.org
\r
3 Delivered-To: notmuch@notmuchmail.org
\r
4 Received: from localhost (localhost [127.0.0.1])
\r
5 by olra.theworths.org (Postfix) with ESMTP id DE2CC431FBC
\r
6 for <notmuch@notmuchmail.org>; Fri, 20 Jul 2012 02:14:08 -0700 (PDT)
\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org
\r
11 X-Spam-Status: No, score=0 tagged_above=-999 required=5 tests=[none]
\r
13 Received: from olra.theworths.org ([127.0.0.1])
\r
14 by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)
\r
15 with ESMTP id 5kNdJfN71GQs for <notmuch@notmuchmail.org>;
\r
16 Fri, 20 Jul 2012 02:14:07 -0700 (PDT)
\r
17 Received: from guru.guru-group.fi (guru.guru-group.fi [46.183.73.34])
\r
18 by olra.theworths.org (Postfix) with ESMTP id 63AA0431FAE
\r
19 for <notmuch@notmuchmail.org>; Fri, 20 Jul 2012 02:14:07 -0700 (PDT)
\r
20 Received: by guru.guru-group.fi (Postfix, from userid 501)
\r
21 id 48B7010014D; Fri, 20 Jul 2012 12:14:18 +0300 (EEST)
\r
22 From: Tomi Ollila <tomi.ollila@iki.fi>
\r
23 To: craven@gmx.net, notmuch@notmuchmail.org
\r
24 Subject: Re: [PATCH v7 3/3] Use the structured formatters in notmuch-search.c.
\r
25 In-Reply-To: <1342766173-1344-4-git-send-email-craven@gmx.net>
\r
26 References: <20120718194819.GP31670@mit.edu>
\r
27 <1342766173-1344-1-git-send-email-craven@gmx.net>
\r
28 <1342766173-1344-4-git-send-email-craven@gmx.net>
\r
29 User-Agent: Notmuch/0.13.2+93~ge4fdd97 (http://notmuchmail.org) Emacs/23.1.1
\r
30 (x86_64-redhat-linux-gnu)
\r
31 X-Face: HhBM'cA~<r"^Xv\KRN0P{vn'Y"Kd;zg_y3S[4)KSN~s?O\"QPoL
\r
32 $[Xv_BD:i/F$WiEWax}R(MPS`^UaptOGD`*/=@\1lKoVa9tnrg0TW?"r7aRtgk[F
\r
33 !)g;OY^,BjTbr)Np:%c_o'jj,Z
\r
34 Date: Fri, 20 Jul 2012 12:14:17 +0300
\r
35 Message-ID: <m2r4s694ly.fsf@guru.guru-group.fi>
\r
37 Content-Type: text/plain; charset=utf-8
\r
38 Content-Transfer-Encoding: quoted-printable
\r
39 X-BeenThere: notmuch@notmuchmail.org
\r
40 X-Mailman-Version: 2.1.13
\r
42 List-Id: "Use and development of the notmuch mail system."
\r
43 <notmuch.notmuchmail.org>
\r
44 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
\r
45 <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
\r
46 List-Archive: <http://notmuchmail.org/pipermail/notmuch>
\r
47 List-Post: <mailto:notmuch@notmuchmail.org>
\r
48 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
\r
49 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
\r
50 <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
\r
51 X-List-Received-Date: Fri, 20 Jul 2012 09:14:09 -0000
\r
53 On Fri, Jul 20 2012, craven@gmx.net wrote:
\r
55 > From: <craven@gmx.net>
\r
57 > This patch switches from the current ad-hoc printer to the structured
\r
58 > formatters in sprinter.h, sprinter-text.c and sprinter-json.c.
\r
60 > The JSON tests are changed slightly in order to make them PASS for the
\r
61 > new structured output formatter.
\r
63 > The text tests pass without adaptation.
\r
65 > notmuch-search.c | 301 ++++++++++++++++---------------------------------=
\r
67 > test/json | 34 ++++---
\r
68 > 2 files changed, 103 insertions(+), 232 deletions(-)
\r
70 > diff --git a/notmuch-search.c b/notmuch-search.c
\r
71 > index 3be296d..07211e8 100644
\r
72 > --- a/notmuch-search.c
\r
73 > +++ b/notmuch-search.c
\r
77 > #include "notmuch-client.h"
\r
78 > +#include "sprinter.h"
\r
82 > @@ -28,92 +29,6 @@ typedef enum {
\r
86 > -typedef struct search_format {
\r
87 > - const char *results_start;
\r
88 > - const char *item_start;
\r
89 > - void (*item_id) (const void *ctx,
\r
90 > - const char *item_type,
\r
91 > - const char *item_id);
\r
92 > - void (*thread_summary) (const void *ctx,
\r
93 > - const char *thread_id,
\r
94 > - const time_t date,
\r
95 > - const int matched,
\r
96 > - const int total,
\r
97 > - const char *authors,
\r
98 > - const char *subject);
\r
99 > - const char *tag_start;
\r
100 > - const char *tag;
\r
101 > - const char *tag_sep;
\r
102 > - const char *tag_end;
\r
103 > - const char *item_sep;
\r
104 > - const char *item_end;
\r
105 > - const char *results_end;
\r
106 > - const char *results_null;
\r
107 > -} search_format_t;
\r
110 > -format_item_id_text (const void *ctx,
\r
111 > - const char *item_type,
\r
112 > - const char *item_id);
\r
115 > -format_thread_text (const void *ctx,
\r
116 > - const char *thread_id,
\r
117 > - const time_t date,
\r
118 > - const int matched,
\r
119 > - const int total,
\r
120 > - const char *authors,
\r
121 > - const char *subject);
\r
122 > -static const search_format_t format_text =3D {
\r
125 > - format_item_id_text,
\r
126 > - format_thread_text,
\r
136 > -format_item_id_json (const void *ctx,
\r
137 > - const char *item_type,
\r
138 > - const char *item_id);
\r
141 > -format_thread_json (const void *ctx,
\r
142 > - const char *thread_id,
\r
143 > - const time_t date,
\r
144 > - const int matched,
\r
145 > - const int total,
\r
146 > - const char *authors,
\r
147 > - const char *subject);
\r
149 > -/* Any changes to the JSON format should be reflected in the file
\r
150 > - * devel/schemata. */
\r
151 > -static const search_format_t format_json =3D {
\r
154 > - format_item_id_json,
\r
155 > - format_thread_json,
\r
157 > - "\"%s\"", ", ",
\r
165 > -format_item_id_text (unused (const void *ctx),
\r
166 > - const char *item_type,
\r
167 > - const char *item_id)
\r
169 > - printf ("%s%s", item_type, item_id);
\r
173 > sanitize_string (const void *ctx, const char *str)
\r
175 > @@ -131,72 +46,8 @@ sanitize_string (const void *ctx, const char *str)
\r
180 > -format_thread_text (const void *ctx,
\r
181 > - const char *thread_id,
\r
182 > - const time_t date,
\r
183 > - const int matched,
\r
184 > - const int total,
\r
185 > - const char *authors,
\r
186 > - const char *subject)
\r
188 > - void *ctx_quote =3D talloc_new (ctx);
\r
190 > - printf ("thread:%s %12s [%d/%d] %s; %s",
\r
192 > - notmuch_time_relative_date (ctx, date),
\r
195 > - sanitize_string (ctx_quote, authors),
\r
196 > - sanitize_string (ctx_quote, subject));
\r
198 > - talloc_free (ctx_quote);
\r
202 > -format_item_id_json (const void *ctx,
\r
203 > - unused (const char *item_type),
\r
204 > - const char *item_id)
\r
206 > - void *ctx_quote =3D talloc_new (ctx);
\r
208 > - printf ("%s", json_quote_str (ctx_quote, item_id));
\r
210 > - talloc_free (ctx_quote);
\r
215 > -format_thread_json (const void *ctx,
\r
216 > - const char *thread_id,
\r
217 > - const time_t date,
\r
218 > - const int matched,
\r
219 > - const int total,
\r
220 > - const char *authors,
\r
221 > - const char *subject)
\r
223 > - void *ctx_quote =3D talloc_new (ctx);
\r
225 > - printf ("\"thread\": %s,\n"
\r
226 > - "\"timestamp\": %ld,\n"
\r
227 > - "\"date_relative\": \"%s\",\n"
\r
228 > - "\"matched\": %d,\n"
\r
229 > - "\"total\": %d,\n"
\r
230 > - "\"authors\": %s,\n"
\r
231 > - "\"subject\": %s,\n",
\r
232 > - json_quote_str (ctx_quote, thread_id),
\r
234 > - notmuch_time_relative_date (ctx, date),
\r
237 > - json_quote_str (ctx_quote, authors),
\r
238 > - json_quote_str (ctx_quote, subject));
\r
240 > - talloc_free (ctx_quote);
\r
244 > -do_search_threads (const search_format_t *format,
\r
245 > +do_search_threads (sprinter_t *format,
\r
246 > notmuch_query_t *query,
\r
247 > notmuch_sort_t sort,
\r
249 > @@ -207,7 +58,6 @@ do_search_threads (const search_format_t *format,
\r
250 > notmuch_threads_t *threads;
\r
251 > notmuch_tags_t *tags;
\r
253 > - int first_thread =3D 1;
\r
256 > if (offset < 0) {
\r
257 > @@ -220,14 +70,12 @@ do_search_threads (const search_format_t *format,
\r
258 > if (threads =3D=3D NULL)
\r
261 > - fputs (format->results_start, stdout);
\r
262 > + format->begin_list (format);
\r
265 > notmuch_threads_valid (threads) && (limit < 0 || i < offset + limit);
\r
266 > notmuch_threads_move_to_next (threads), i++)
\r
268 > - int first_tag =3D 1;
\r
270 > thread =3D notmuch_threads_get (threads);
\r
272 > if (i < offset) {
\r
273 > @@ -235,60 +83,97 @@ do_search_threads (const search_format_t *format,
\r
277 > - if (! first_thread)
\r
278 > - fputs (format->item_sep, stdout);
\r
280 > if (output =3D=3D OUTPUT_THREADS) {
\r
281 > - format->item_id (thread, "thread:",
\r
282 > - notmuch_thread_get_thread_id (thread));
\r
283 > + format->set_prefix (format, "thread");
\r
284 > + format->string (format,
\r
285 > + notmuch_thread_get_thread_id (thread));
\r
286 > + format->separator (format);
\r
287 > } else { /* output =3D=3D OUTPUT_SUMMARY */
\r
288 > - fputs (format->item_start, stdout);
\r
289 > + void *ctx_quote =3D talloc_new (thread);
\r
290 > + const char *authors =3D notmuch_thread_get_authors (thread);
\r
291 > + const char *subject =3D notmuch_thread_get_subject (thread);
\r
292 > + const char *thread_id =3D notmuch_thread_get_thread_id (thread);
\r
293 > + int matched =3D notmuch_thread_get_matched_messages (thread);
\r
294 > + int total =3D notmuch_thread_get_total_messages (thread);
\r
295 > + const char *relative_date =3D NULL;
\r
296 > + notmuch_bool_t first_tag =3D TRUE;
\r
298 > + format->begin_map (format);
\r
300 > if (sort =3D=3D NOTMUCH_SORT_OLDEST_FIRST)
\r
301 > date =3D notmuch_thread_get_oldest_date (thread);
\r
303 > date =3D notmuch_thread_get_newest_date (thread);
\r
305 > - format->thread_summary (thread,
\r
306 > - notmuch_thread_get_thread_id (thread),
\r
308 > - notmuch_thread_get_matched_messages (thread),
\r
309 > - notmuch_thread_get_total_messages (thread),
\r
310 > - notmuch_thread_get_authors (thread),
\r
311 > - notmuch_thread_get_subject (thread));
\r
312 > + relative_date =3D notmuch_time_relative_date (ctx_quote, date);
\r
314 > + if (format->is_text_printer) {
\r
315 > + /* Special case for the text formatter */
\r
316 > + printf ("thread:%s %12s [%d/%d] %s; %s (",
\r
321 > + sanitize_string (ctx_quote, authors),
\r
322 > + sanitize_string (ctx_quote, subject));
\r
323 > + } else { /* Structured Output */
\r
324 > + format->map_key (format, "thread");
\r
325 > + format->string (format, thread_id);
\r
326 > + format->map_key (format, "timestamp");
\r
327 > + format->integer (format, date);
\r
328 > + format->map_key (format, "date_relative");
\r
329 > + format->string (format, relative_date);
\r
330 > + format->map_key (format, "matched");
\r
331 > + format->integer (format, matched);
\r
332 > + format->map_key (format, "total");
\r
333 > + format->integer (format, total);
\r
334 > + format->map_key (format, "authors");
\r
335 > + format->string (format, authors);
\r
336 > + format->map_key (format, "subject");
\r
337 > + format->string (format, subject);
\r
340 > + talloc_free (ctx_quote);
\r
342 > - fputs (format->tag_start, stdout);
\r
343 > + format->map_key (format, "tags");
\r
344 > + format->begin_list (format);
\r
346 > for (tags =3D notmuch_thread_get_tags (thread);
\r
347 > notmuch_tags_valid (tags);
\r
348 > notmuch_tags_move_to_next (tags))
\r
350 > - if (! first_tag)
\r
351 > - fputs (format->tag_sep, stdout);
\r
352 > - printf (format->tag, notmuch_tags_get (tags));
\r
353 > - first_tag =3D 0;
\r
354 > + const char *tag =3D notmuch_tags_get (tags);
\r
356 > + if (format->is_text_printer) {
\r
357 > + /* Special case for the text formatter */
\r
359 > + first_tag =3D FALSE;
\r
361 > + fputc (' ', stdout);
\r
362 > + fputs (tag, stdout);
\r
363 > + } else { /* Structured Output */
\r
364 > + format->string (format, tag);
\r
368 > - fputs (format->tag_end, stdout);
\r
369 > + if (format->is_text_printer)
\r
372 > - fputs (format->item_end, stdout);
\r
373 > + format->end (format);
\r
374 > + format->end (format);
\r
375 > + format->separator (format);
\r
378 > - first_thread =3D 0;
\r
380 > notmuch_thread_destroy (thread);
\r
383 > - if (first_thread)
\r
384 > - fputs (format->results_null, stdout);
\r
386 > - fputs (format->results_end, stdout);
\r
387 > + format->end (format);
\r
393 > -do_search_messages (const search_format_t *format,
\r
394 > +do_search_messages (sprinter_t *format,
\r
395 > notmuch_query_t *query,
\r
398 > @@ -297,7 +182,6 @@ do_search_messages (const search_format_t *format,
\r
399 > notmuch_message_t *message;
\r
400 > notmuch_messages_t *messages;
\r
401 > notmuch_filenames_t *filenames;
\r
402 > - int first_message =3D 1;
\r
405 > if (offset < 0) {
\r
406 > @@ -310,7 +194,7 @@ do_search_messages (const search_format_t *format,
\r
407 > if (messages =3D=3D NULL)
\r
410 > - fputs (format->results_start, stdout);
\r
411 > + format->begin_list (format);
\r
414 > notmuch_messages_valid (messages) && (limit < 0 || i < offset + limit);
\r
415 > @@ -328,24 +212,17 @@ do_search_messages (const search_format_t *format,
\r
416 > notmuch_filenames_valid (filenames);
\r
417 > notmuch_filenames_move_to_next (filenames))
\r
419 > - if (! first_message)
\r
420 > - fputs (format->item_sep, stdout);
\r
422 > - format->item_id (message, "",
\r
423 > - notmuch_filenames_get (filenames));
\r
425 > - first_message =3D 0;
\r
426 > + format->string (format, notmuch_filenames_get (filenames));
\r
427 > + format->separator (format);
\r
429 >=20=20=09=20=20=20=20
\r
430 > notmuch_filenames_destroy( filenames );
\r
432 > } else { /* output =3D=3D OUTPUT_MESSAGES */
\r
433 > - if (! first_message)
\r
434 > - fputs (format->item_sep, stdout);
\r
436 > - format->item_id (message, "id:",
\r
437 > - notmuch_message_get_message_id (message));
\r
438 > - first_message =3D 0;
\r
439 > + format->set_prefix (format, "id");
\r
440 > + format->string (format,
\r
441 > + notmuch_message_get_message_id (message));
\r
442 > + format->separator (format);
\r
445 > notmuch_message_destroy (message);
\r
446 > @@ -353,23 +230,19 @@ do_search_messages (const search_format_t *format,
\r
448 > notmuch_messages_destroy (messages);
\r
450 > - if (first_message)
\r
451 > - fputs (format->results_null, stdout);
\r
453 > - fputs (format->results_end, stdout);
\r
454 > + format->end (format);
\r
460 > do_search_tags (notmuch_database_t *notmuch,
\r
461 > - const search_format_t *format,
\r
462 > + sprinter_t *format,
\r
463 > notmuch_query_t *query)
\r
465 > notmuch_messages_t *messages =3D NULL;
\r
466 > notmuch_tags_t *tags;
\r
468 > - int first_tag =3D 1;
\r
470 > /* should the following only special case if no excluded terms
\r
472 > @@ -387,7 +260,7 @@ do_search_tags (notmuch_database_t *notmuch,
\r
473 > if (tags =3D=3D NULL)
\r
476 > - fputs (format->results_start, stdout);
\r
477 > + format->begin_list (format);
\r
480 > notmuch_tags_valid (tags);
\r
481 > @@ -395,12 +268,9 @@ do_search_tags (notmuch_database_t *notmuch,
\r
483 > tag =3D notmuch_tags_get (tags);
\r
485 > - if (! first_tag)
\r
486 > - fputs (format->item_sep, stdout);
\r
487 > + format->string (format, tag);
\r
488 > + format->separator (format);
\r
490 > - format->item_id (tags, "", tag);
\r
492 > - first_tag =3D 0;
\r
495 > notmuch_tags_destroy (tags);
\r
496 > @@ -408,10 +278,7 @@ do_search_tags (notmuch_database_t *notmuch,
\r
498 > notmuch_messages_destroy (messages);
\r
501 > - fputs (format->results_null, stdout);
\r
503 > - fputs (format->results_end, stdout);
\r
504 > + format->end (format);
\r
508 > @@ -430,7 +297,7 @@ notmuch_search_command (void *ctx, int argc, char *ar=
\r
510 > notmuch_query_t *query;
\r
512 > notmuch_sort_t sort =3D NOTMUCH_SORT_NEWEST_FIRST;
\r
513 > - const search_format_t *format =3D &format_text;
\r
514 > + sprinter_t *format =3D NULL;
\r
516 due to this change -- dropping "fallback" setting due to ...
\r
518 > int opt_index, ret;
\r
519 > output_t output =3D OUTPUT_SUMMARY;
\r
520 > int offset =3D 0;
\r
521 > @@ -475,10 +342,10 @@ notmuch_search_command (void *ctx, int argc, char *=
\r
524 > switch (format_sel) {
\r
525 > case NOTMUCH_FORMAT_TEXT:
\r
526 > - format =3D &format_text;
\r
527 > + format =3D sprinter_text_create (ctx, stdout);
\r
529 > case NOTMUCH_FORMAT_JSON:
\r
530 > - format =3D &format_json;
\r
531 > + format =3D sprinter_json_create (ctx, stdout);
\r
535 ... the dynamically created structure above, should we have 'default:'
\r
536 case which executes INTERNAL_ERROR() (or something) instead of allowing
\r
537 NULL derefencing (and crash) if one ever encounters such a situation ?
\r
542 > @@ -546,5 +413,7 @@ notmuch_search_command (void *ctx, int argc, char *ar=
\r
544 > notmuch_query_destroy (query);
\r
545 > notmuch_database_destroy (notmuch);
\r
547 > + talloc_free (format);
\r
551 > diff --git a/test/json b/test/json
\r
552 > index 6439788..337b3f5 100755
\r
555 > @@ -9,15 +9,16 @@ test_expect_equal "$output" "[[[{\"id\": \"${gen_msg_id=
\r
556 }\", \"match\": true, \"e
\r
558 > test_begin_subtest "Search message: json"
\r
559 > add_message "[subject]=3D\"json-search-subject\"" "[date]=3D\"Sat, 01 Ja=
\r
560 n 2000 12:00:00 -0000\"" "[body]=3D\"json-search-message\""
\r
561 > -output=3D$(notmuch search --format=3Djson "json-search-message" | notmuc=
\r
563 > +output=3D$(notmuch search --format=3Djson "json-search-message" | notmuc=
\r
564 h_json_show_sanitize | notmuch_search_sanitize)
\r
565 > test_expect_equal "$output" "[{\"thread\": \"XXX\",
\r
566 > -\"timestamp\": 946728000,
\r
567 > -\"date_relative\": \"2000-01-01\",
\r
570 > -\"authors\": \"Notmuch Test Suite\",
\r
571 > -\"subject\": \"json-search-subject\",
\r
572 > -\"tags\": [\"inbox\", \"unread\"]}]"
\r
573 > + \"timestamp\": 946728000,
\r
574 > + \"date_relative\": \"2000-01-01\",
\r
575 > + \"matched\": 1,
\r
577 > + \"authors\": \"Notmuch Test Suite\",
\r
578 > + \"subject\": \"json-search-subject\",
\r
579 > + \"tags\": [\"inbox\",
\r
582 > test_begin_subtest "Show message: json, utf-8"
\r
583 > add_message "[subject]=3D\"json-show-utf8-body-s=C3=BCbj=C3=A9ct\"" "[da=
\r
584 te]=3D\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=3D\"js=C3=B6n-show-m=C3=
\r
586 > @@ -39,14 +40,15 @@ test_expect_equal "$output" "[[[{\"id\": \"$id\", \"m=
\r
587 atch\": true, \"excluded\":
\r
589 > test_begin_subtest "Search message: json, utf-8"
\r
590 > add_message "[subject]=3D\"json-search-utf8-body-s=C3=BCbj=C3=A9ct\"" "[=
\r
591 date]=3D\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=3D\"js=C3=B6n-search-m=
\r
593 > -output=3D$(notmuch search --format=3Djson "js=C3=B6n-search-m=C3=A9ssage=
\r
594 " | notmuch_search_sanitize)
\r
595 > +output=3D$(notmuch search --format=3Djson "js=C3=B6n-search-m=C3=A9ssage=
\r
596 " | notmuch_json_show_sanitize | notmuch_search_sanitize)
\r
597 > test_expect_equal "$output" "[{\"thread\": \"XXX\",
\r
598 > -\"timestamp\": 946728000,
\r
599 > -\"date_relative\": \"2000-01-01\",
\r
602 > -\"authors\": \"Notmuch Test Suite\",
\r
603 > -\"subject\": \"json-search-utf8-body-s=C3=BCbj=C3=A9ct\",
\r
604 > -\"tags\": [\"inbox\", \"unread\"]}]"
\r
605 > + \"timestamp\": 946728000,
\r
606 > + \"date_relative\": \"2000-01-01\",
\r
607 > + \"matched\": 1,
\r
609 > + \"authors\": \"Notmuch Test Suite\",
\r
610 > + \"subject\": \"json-search-utf8-body-s=C3=BCbj=C3=A9ct\",
\r
611 > + \"tags\": [\"inbox\",
\r
618 > _______________________________________________
\r
619 > notmuch mailing list
\r
620 > notmuch@notmuchmail.org
\r
621 > http://notmuchmail.org/mailman/listinfo/notmuch
\r