Quoth Peter Feigl on Jan 21 at 10:16 pm:
> The output routines have been rewritten so that logical structure
> (objects with key/value pairs, arrays, strings and numbers) are
> written instead of ad-hoc printfs. This allows for easier adaptation
> of other output formats, as only the routines that start/end an object
> etc. have to be rewritten. The logic is the same for all formats.
> The default text output is handled differently, special cases are
> inserted at the proper places, as it differs too much from the
> structured output.
I think this is a great idea and I'm a fan of having an S-expression
format, but I also think there's a much easier and more general way to
structure this.
In particular, I don't think you should hijack search_format, since
you'll wind up repeating most of this work for anything else that
needs to output structured data (notmuch show, at least). Rather, I'd
suggest creating a general "structure printer" struct that isn't tied
to search. You've essentially already done this, you just called it
search_format. Then, leave the existing format callbacks in place,
just use this new API instead of lots of printf calls.
What about all of those annoying {tag,item}_{start,sep,end} fields? I
think you can simultaneously get rid of those *and* simplify the
structure printer API. If the structure printer is allowed to keep a
little state, it can automatically insert separators. With a little
nesting state, it could even insert terminators by just saying "pop me
to this level". This could probably be better, but I'm imagining
something like
struct sprinter *
new_json_sprinter (const void *ctx, FILE *stream);
struct sprinter *
new_sexp_sprinter (const void *ctx, FILE *stream);
/* Start a map (a JSON object or a S-expression alist/plist/whatever)
* and return the nesting level of the map. */
int
sprinter_map (struct sprinter *sp);
/* Start a list (aka array) and return the nesting level of the list. */
int
sprinter_list (struct sprinter *sp);
/* Close maps and lists until reaching level. */
void
sprinter_pop (struct sprinter *sp, int level);
void
sprinter_map_key (struct sprinter *sp, const char *key);
void
sprinter_number (struct sprinter *sp, int val);
void
sprinter_string (struct sprinter *sp, const char *val);
void
sprinter_bool (struct sprinter *sp, notmuch_bool_t val);
and that's it. This would also subsume your format_attribute_*
helpers.
Unfortunately, it's a pain to pass things like a structure printer
object around formatters (too bad notmuch isn't written in C++, eh?).
I think it's better to address this than to structure around it.
Probably the simplest thing to do is to make a struct for formatter
state and pass that in to the callbacks. You could also more
completely emulate classes, but that would probably be overkill for
this.