mirror of
https://git.notmuchmail.org/git/notmuch
synced 2024-11-24 20:08:10 +01:00
lib: factor out prefix related code to its own file
Reduce the size of database.cc, and limit the scope of prefix_table, make sure it's accessed via a well-defined internal API.
This commit is contained in:
parent
e34e2a68b6
commit
3b40978241
4 changed files with 227 additions and 194 deletions
|
@ -60,7 +60,8 @@ libnotmuch_cxx_srcs = \
|
|||
$(dir)/regexp-fields.cc \
|
||||
$(dir)/thread.cc \
|
||||
$(dir)/thread-fp.cc \
|
||||
$(dir)/features.cc
|
||||
$(dir)/features.cc \
|
||||
$(dir)/prefix.cc
|
||||
|
||||
libnotmuch_modules := $(libnotmuch_c_srcs:.c=.o) $(libnotmuch_cxx_srcs:.cc=.o)
|
||||
|
||||
|
|
|
@ -277,4 +277,11 @@ _notmuch_database_parse_features (const void *ctx, const char *features, unsigne
|
|||
char *
|
||||
_notmuch_database_print_features (const void *ctx, unsigned int features);
|
||||
|
||||
/* prefix.cc */
|
||||
notmuch_status_t
|
||||
_notmuch_database_setup_standard_query_fields (notmuch_database_t *notmuch);
|
||||
|
||||
notmuch_status_t
|
||||
_notmuch_database_setup_user_query_fields (notmuch_database_t *notmuch);
|
||||
|
||||
#endif
|
||||
|
|
201
lib/database.cc
201
lib/database.cc
|
@ -263,80 +263,6 @@ _notmuch_database_mode (notmuch_database_t *notmuch)
|
|||
* same thread.
|
||||
*/
|
||||
|
||||
/* With these prefix values we follow the conventions published here:
|
||||
*
|
||||
* https://xapian.org/docs/omega/termprefixes.html
|
||||
*
|
||||
* as much as makes sense. Note that I took some liberty in matching
|
||||
* the reserved prefix values to notmuch concepts, (for example, 'G'
|
||||
* is documented as "newsGroup (or similar entity - e.g. a web forum
|
||||
* name)", for which I think the thread is the closest analogue in
|
||||
* notmuch. This in spite of the fact that we will eventually be
|
||||
* storing mailing-list messages where 'G' for "mailing list name"
|
||||
* might be even a closer analogue. I'm treating the single-character
|
||||
* prefixes preferentially for core notmuch concepts (which will be
|
||||
* nearly universal to all mail messages).
|
||||
*/
|
||||
|
||||
static const
|
||||
prefix_t prefix_table[] = {
|
||||
/* name term prefix flags */
|
||||
{ "type", "T", NOTMUCH_FIELD_NO_FLAGS },
|
||||
{ "reference", "XREFERENCE", NOTMUCH_FIELD_NO_FLAGS },
|
||||
{ "replyto", "XREPLYTO", NOTMUCH_FIELD_NO_FLAGS },
|
||||
{ "directory", "XDIRECTORY", NOTMUCH_FIELD_NO_FLAGS },
|
||||
{ "file-direntry", "XFDIRENTRY", NOTMUCH_FIELD_NO_FLAGS },
|
||||
{ "directory-direntry", "XDDIRENTRY", NOTMUCH_FIELD_NO_FLAGS },
|
||||
{ "body", "", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROBABILISTIC },
|
||||
{ "thread", "G", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROCESSOR },
|
||||
{ "tag", "K", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROCESSOR },
|
||||
{ "is", "K", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROCESSOR },
|
||||
{ "id", "Q", NOTMUCH_FIELD_EXTERNAL },
|
||||
{ "mid", "Q", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROCESSOR },
|
||||
{ "path", "P", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROCESSOR },
|
||||
{ "property", "XPROPERTY", NOTMUCH_FIELD_EXTERNAL },
|
||||
/*
|
||||
* Unconditionally add ':' to reduce potential ambiguity with
|
||||
* overlapping prefixes and/or terms that start with capital
|
||||
* letters. See Xapian document termprefixes.html for related
|
||||
* discussion.
|
||||
*/
|
||||
{ "folder", "XFOLDER:", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROCESSOR },
|
||||
{ "date", NULL, NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROCESSOR },
|
||||
{ "query", NULL, NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROCESSOR },
|
||||
{ "from", "XFROM", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROBABILISTIC |
|
||||
NOTMUCH_FIELD_PROCESSOR },
|
||||
{ "to", "XTO", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROBABILISTIC },
|
||||
{ "attachment", "XATTACHMENT", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROBABILISTIC },
|
||||
{ "mimetype", "XMIMETYPE", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROBABILISTIC },
|
||||
{ "subject", "XSUBJECT", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROBABILISTIC |
|
||||
NOTMUCH_FIELD_PROCESSOR },
|
||||
};
|
||||
|
||||
static void
|
||||
_setup_query_field_default (const prefix_t *prefix, notmuch_database_t *notmuch)
|
||||
{
|
||||
if (prefix->prefix)
|
||||
notmuch->query_parser->add_prefix ("", prefix->prefix);
|
||||
if (prefix->flags & NOTMUCH_FIELD_PROBABILISTIC)
|
||||
notmuch->query_parser->add_prefix (prefix->name, prefix->prefix);
|
||||
else
|
||||
notmuch->query_parser->add_boolean_prefix (prefix->name, prefix->prefix);
|
||||
}
|
||||
|
||||
notmuch_string_map_iterator_t *
|
||||
_notmuch_database_user_headers (notmuch_database_t *notmuch)
|
||||
|
@ -344,118 +270,6 @@ _notmuch_database_user_headers (notmuch_database_t *notmuch)
|
|||
return _notmuch_string_map_iterator_create (notmuch->user_header, "", false);
|
||||
}
|
||||
|
||||
const char *
|
||||
_user_prefix (void *ctx, const char *name)
|
||||
{
|
||||
return talloc_asprintf (ctx, "XU%s:", name);
|
||||
}
|
||||
|
||||
static notmuch_status_t
|
||||
_setup_user_query_fields (notmuch_database_t *notmuch)
|
||||
{
|
||||
notmuch_config_list_t *list;
|
||||
notmuch_status_t status;
|
||||
|
||||
notmuch->user_prefix = _notmuch_string_map_create (notmuch);
|
||||
if (notmuch->user_prefix == NULL)
|
||||
return NOTMUCH_STATUS_OUT_OF_MEMORY;
|
||||
|
||||
notmuch->user_header = _notmuch_string_map_create (notmuch);
|
||||
if (notmuch->user_header == NULL)
|
||||
return NOTMUCH_STATUS_OUT_OF_MEMORY;
|
||||
|
||||
status = notmuch_database_get_config_list (notmuch, CONFIG_HEADER_PREFIX, &list);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) {
|
||||
|
||||
prefix_t query_field;
|
||||
|
||||
const char *key = notmuch_config_list_key (list)
|
||||
+ sizeof (CONFIG_HEADER_PREFIX) - 1;
|
||||
|
||||
_notmuch_string_map_append (notmuch->user_prefix,
|
||||
key,
|
||||
_user_prefix (notmuch, key));
|
||||
|
||||
_notmuch_string_map_append (notmuch->user_header,
|
||||
key,
|
||||
notmuch_config_list_value (list));
|
||||
|
||||
query_field.name = talloc_strdup (notmuch, key);
|
||||
query_field.prefix = _user_prefix (notmuch, key);
|
||||
query_field.flags = NOTMUCH_FIELD_PROBABILISTIC
|
||||
| NOTMUCH_FIELD_EXTERNAL;
|
||||
|
||||
_setup_query_field_default (&query_field, notmuch);
|
||||
}
|
||||
|
||||
notmuch_config_list_destroy (list);
|
||||
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
_setup_query_field (const prefix_t *prefix, notmuch_database_t *notmuch)
|
||||
{
|
||||
if (prefix->flags & NOTMUCH_FIELD_PROCESSOR) {
|
||||
Xapian::FieldProcessor *fp;
|
||||
|
||||
if (STRNCMP_LITERAL (prefix->name, "date") == 0)
|
||||
fp = (new DateFieldProcessor(NOTMUCH_VALUE_TIMESTAMP))->release ();
|
||||
else if (STRNCMP_LITERAL(prefix->name, "query") == 0)
|
||||
fp = (new QueryFieldProcessor (*notmuch->query_parser, notmuch))->release ();
|
||||
else if (STRNCMP_LITERAL (prefix->name, "thread") == 0)
|
||||
fp = (new ThreadFieldProcessor (*notmuch->query_parser, notmuch))->release ();
|
||||
else
|
||||
fp = (new RegexpFieldProcessor (prefix->name, prefix->flags,
|
||||
*notmuch->query_parser, notmuch))->release ();
|
||||
|
||||
/* we treat all field-processor fields as boolean in order to get the raw input */
|
||||
if (prefix->prefix)
|
||||
notmuch->query_parser->add_prefix ("", prefix->prefix);
|
||||
notmuch->query_parser->add_boolean_prefix (prefix->name, fp);
|
||||
} else {
|
||||
_setup_query_field_default (prefix, notmuch);
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
_find_prefix (const char *name)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE (prefix_table); i++) {
|
||||
if (strcmp (name, prefix_table[i].name) == 0)
|
||||
return prefix_table[i].prefix;
|
||||
}
|
||||
|
||||
INTERNAL_ERROR ("No prefix exists for '%s'\n", name);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Like find prefix, but include the possibility of user defined
|
||||
* prefixes specific to this database */
|
||||
|
||||
const char *
|
||||
_notmuch_database_prefix (notmuch_database_t *notmuch, const char *name)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/*XXX TODO: reduce code duplication */
|
||||
for (i = 0; i < ARRAY_SIZE (prefix_table); i++) {
|
||||
if (strcmp (name, prefix_table[i].name) == 0)
|
||||
return prefix_table[i].prefix;
|
||||
}
|
||||
|
||||
if (notmuch->user_prefix)
|
||||
return _notmuch_string_map_get (notmuch->user_prefix, name);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
notmuch_status_to_string (notmuch_status_t status)
|
||||
{
|
||||
|
@ -952,13 +766,14 @@ notmuch_database_open_verbose (const char *path,
|
|||
notmuch->query_parser->add_rangeprocessor (notmuch->date_range_processor);
|
||||
notmuch->query_parser->add_rangeprocessor (notmuch->last_mod_range_processor);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE (prefix_table); i++) {
|
||||
const prefix_t *prefix = &prefix_table[i];
|
||||
if (prefix->flags & NOTMUCH_FIELD_EXTERNAL) {
|
||||
_setup_query_field (prefix, notmuch);
|
||||
}
|
||||
}
|
||||
status = _setup_user_query_fields (notmuch);
|
||||
status = _notmuch_database_setup_standard_query_fields (notmuch);
|
||||
if (status)
|
||||
goto DONE;
|
||||
|
||||
status = _notmuch_database_setup_user_query_fields (notmuch);
|
||||
if (status)
|
||||
goto DONE;
|
||||
|
||||
} catch (const Xapian::Error &error) {
|
||||
IGNORE_RESULT (asprintf (&message, "A Xapian exception occurred opening database: %s\n",
|
||||
error.get_msg ().c_str ()));
|
||||
|
|
210
lib/prefix.cc
Normal file
210
lib/prefix.cc
Normal file
|
@ -0,0 +1,210 @@
|
|||
#include "database-private.h"
|
||||
#include "query-fp.h"
|
||||
#include "thread-fp.h"
|
||||
#include "regexp-fields.h"
|
||||
#include "parse-time-vrp.h"
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const char *prefix;
|
||||
notmuch_field_flag_t flags;
|
||||
} prefix_t;
|
||||
|
||||
/* With these prefix values we follow the conventions published here:
|
||||
*
|
||||
* https://xapian.org/docs/omega/termprefixes.html
|
||||
*
|
||||
* as much as makes sense. Note that I took some liberty in matching
|
||||
* the reserved prefix values to notmuch concepts, (for example, 'G'
|
||||
* is documented as "newsGroup (or similar entity - e.g. a web forum
|
||||
* name)", for which I think the thread is the closest analogue in
|
||||
* notmuch. This in spite of the fact that we will eventually be
|
||||
* storing mailing-list messages where 'G' for "mailing list name"
|
||||
* might be even a closer analogue. I'm treating the single-character
|
||||
* prefixes preferentially for core notmuch concepts (which will be
|
||||
* nearly universal to all mail messages).
|
||||
*/
|
||||
|
||||
static const
|
||||
prefix_t prefix_table[] = {
|
||||
/* name term prefix flags */
|
||||
{ "type", "T", NOTMUCH_FIELD_NO_FLAGS },
|
||||
{ "reference", "XREFERENCE", NOTMUCH_FIELD_NO_FLAGS },
|
||||
{ "replyto", "XREPLYTO", NOTMUCH_FIELD_NO_FLAGS },
|
||||
{ "directory", "XDIRECTORY", NOTMUCH_FIELD_NO_FLAGS },
|
||||
{ "file-direntry", "XFDIRENTRY", NOTMUCH_FIELD_NO_FLAGS },
|
||||
{ "directory-direntry", "XDDIRENTRY", NOTMUCH_FIELD_NO_FLAGS },
|
||||
{ "body", "", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROBABILISTIC },
|
||||
{ "thread", "G", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROCESSOR },
|
||||
{ "tag", "K", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROCESSOR },
|
||||
{ "is", "K", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROCESSOR },
|
||||
{ "id", "Q", NOTMUCH_FIELD_EXTERNAL },
|
||||
{ "mid", "Q", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROCESSOR },
|
||||
{ "path", "P", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROCESSOR },
|
||||
{ "property", "XPROPERTY", NOTMUCH_FIELD_EXTERNAL },
|
||||
/*
|
||||
* Unconditionally add ':' to reduce potential ambiguity with
|
||||
* overlapping prefixes and/or terms that start with capital
|
||||
* letters. See Xapian document termprefixes.html for related
|
||||
* discussion.
|
||||
*/
|
||||
{ "folder", "XFOLDER:", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROCESSOR },
|
||||
{ "date", NULL, NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROCESSOR },
|
||||
{ "query", NULL, NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROCESSOR },
|
||||
{ "from", "XFROM", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROBABILISTIC |
|
||||
NOTMUCH_FIELD_PROCESSOR },
|
||||
{ "to", "XTO", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROBABILISTIC },
|
||||
{ "attachment", "XATTACHMENT", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROBABILISTIC },
|
||||
{ "mimetype", "XMIMETYPE", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROBABILISTIC },
|
||||
{ "subject", "XSUBJECT", NOTMUCH_FIELD_EXTERNAL |
|
||||
NOTMUCH_FIELD_PROBABILISTIC |
|
||||
NOTMUCH_FIELD_PROCESSOR },
|
||||
};
|
||||
|
||||
static const char *
|
||||
_user_prefix (void *ctx, const char *name)
|
||||
{
|
||||
return talloc_asprintf (ctx, "XU%s:", name);
|
||||
}
|
||||
|
||||
const char *
|
||||
_find_prefix (const char *name)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE (prefix_table); i++) {
|
||||
if (strcmp (name, prefix_table[i].name) == 0)
|
||||
return prefix_table[i].prefix;
|
||||
}
|
||||
|
||||
INTERNAL_ERROR ("No prefix exists for '%s'\n", name);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Like find prefix, but include the possibility of user defined
|
||||
* prefixes specific to this database */
|
||||
|
||||
const char *
|
||||
_notmuch_database_prefix (notmuch_database_t *notmuch, const char *name)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/*XXX TODO: reduce code duplication */
|
||||
for (i = 0; i < ARRAY_SIZE (prefix_table); i++) {
|
||||
if (strcmp (name, prefix_table[i].name) == 0)
|
||||
return prefix_table[i].prefix;
|
||||
}
|
||||
|
||||
if (notmuch->user_prefix)
|
||||
return _notmuch_string_map_get (notmuch->user_prefix, name);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
_setup_query_field_default (const prefix_t *prefix, notmuch_database_t *notmuch)
|
||||
{
|
||||
if (prefix->prefix)
|
||||
notmuch->query_parser->add_prefix ("", prefix->prefix);
|
||||
if (prefix->flags & NOTMUCH_FIELD_PROBABILISTIC)
|
||||
notmuch->query_parser->add_prefix (prefix->name, prefix->prefix);
|
||||
else
|
||||
notmuch->query_parser->add_boolean_prefix (prefix->name, prefix->prefix);
|
||||
}
|
||||
|
||||
static void
|
||||
_setup_query_field (const prefix_t *prefix, notmuch_database_t *notmuch)
|
||||
{
|
||||
if (prefix->flags & NOTMUCH_FIELD_PROCESSOR) {
|
||||
Xapian::FieldProcessor *fp;
|
||||
|
||||
if (STRNCMP_LITERAL (prefix->name, "date") == 0)
|
||||
fp = (new DateFieldProcessor(NOTMUCH_VALUE_TIMESTAMP))->release ();
|
||||
else if (STRNCMP_LITERAL(prefix->name, "query") == 0)
|
||||
fp = (new QueryFieldProcessor (*notmuch->query_parser, notmuch))->release ();
|
||||
else if (STRNCMP_LITERAL (prefix->name, "thread") == 0)
|
||||
fp = (new ThreadFieldProcessor (*notmuch->query_parser, notmuch))->release ();
|
||||
else
|
||||
fp = (new RegexpFieldProcessor (prefix->name, prefix->flags,
|
||||
*notmuch->query_parser, notmuch))->release ();
|
||||
|
||||
/* we treat all field-processor fields as boolean in order to get the raw input */
|
||||
if (prefix->prefix)
|
||||
notmuch->query_parser->add_prefix ("", prefix->prefix);
|
||||
notmuch->query_parser->add_boolean_prefix (prefix->name, fp);
|
||||
} else {
|
||||
_setup_query_field_default (prefix, notmuch);
|
||||
}
|
||||
}
|
||||
|
||||
notmuch_status_t
|
||||
_notmuch_database_setup_standard_query_fields (notmuch_database_t *notmuch)
|
||||
{
|
||||
for (unsigned int i = 0; i < ARRAY_SIZE (prefix_table); i++) {
|
||||
const prefix_t *prefix = &prefix_table[i];
|
||||
if (prefix->flags & NOTMUCH_FIELD_EXTERNAL) {
|
||||
_setup_query_field (prefix, notmuch);
|
||||
}
|
||||
}
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
notmuch_status_t
|
||||
_notmuch_database_setup_user_query_fields (notmuch_database_t *notmuch)
|
||||
{
|
||||
notmuch_config_list_t *list;
|
||||
notmuch_status_t status;
|
||||
|
||||
notmuch->user_prefix = _notmuch_string_map_create (notmuch);
|
||||
if (notmuch->user_prefix == NULL)
|
||||
return NOTMUCH_STATUS_OUT_OF_MEMORY;
|
||||
|
||||
notmuch->user_header = _notmuch_string_map_create (notmuch);
|
||||
if (notmuch->user_header == NULL)
|
||||
return NOTMUCH_STATUS_OUT_OF_MEMORY;
|
||||
|
||||
status = notmuch_database_get_config_list (notmuch, CONFIG_HEADER_PREFIX, &list);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) {
|
||||
|
||||
prefix_t query_field;
|
||||
|
||||
const char *key = notmuch_config_list_key (list)
|
||||
+ sizeof (CONFIG_HEADER_PREFIX) - 1;
|
||||
|
||||
_notmuch_string_map_append (notmuch->user_prefix,
|
||||
key,
|
||||
_user_prefix (notmuch, key));
|
||||
|
||||
_notmuch_string_map_append (notmuch->user_header,
|
||||
key,
|
||||
notmuch_config_list_value (list));
|
||||
|
||||
query_field.name = talloc_strdup (notmuch, key);
|
||||
query_field.prefix = _user_prefix (notmuch, key);
|
||||
query_field.flags = NOTMUCH_FIELD_PROBABILISTIC
|
||||
| NOTMUCH_FIELD_EXTERNAL;
|
||||
|
||||
_setup_query_field_default (&query_field, notmuch);
|
||||
}
|
||||
|
||||
notmuch_config_list_destroy (list);
|
||||
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
}
|
Loading…
Reference in a new issue