| File src/ItemFilter.cpp changed (mode: 100644) (index 46c3eff..56abf20) | 
	| ... | ... | ItemFilter::~ItemFilter() | 
	| 53 | 53 | bool | bool | 
	| 54 | 54 | ItemFilter::passes(Item &item) const | ItemFilter::passes(Item &item) const | 
	| 55 | 55 | { | { | 
	| 56 |  | return passes([&item](const std::string &f) { return item.getValue(f); }); |  | 
	|  | 56 |  | return passes([&item](const std::string &f) { | 
	|  | 57 |  | return std::vector<std::string>{ item.getValue(f) }; | 
	|  | 58 |  | }); | 
	| 57 | 59 | } | } | 
	| 58 | 60 |  |  | 
	| 59 | 61 | bool | bool | 
	| 60 |  | ItemFilter::passes(std::function<std::string(const std::string &)> accessor) |  | 
	| 61 |  | const |  | 
	|  | 62 |  | ItemFilter::passes(std::function<accessor_f> accessor) const | 
	| 62 | 63 | { | { | 
	| 63 | 64 | std::string error; | std::string error; | 
	| 64 | 65 | return passes(accessor, error); | return passes(accessor, error); | 
	| 65 | 66 | } | } | 
	| 66 | 67 |  |  | 
	| 67 | 68 | bool | bool | 
	| 68 |  | ItemFilter::passes(std::function<std::string(const std::string &)> accessor, |  | 
	| 69 |  | std::string &error) const |  | 
	|  | 69 |  | ItemFilter::passes(std::function<accessor_f> accessor, std::string &error) const | 
	| 70 | 70 | { | { | 
	| 71 | 71 | error.clear(); | error.clear(); | 
	| 72 | 72 |  |  | 
|  | 
	| ... | ... | ItemFilter::passes(std::function<std::string(const std::string &)> accessor, | 
	| 89 | 89 | }; | }; | 
	| 90 | 90 |  |  | 
	| 91 | 91 | for (const Cond &cond : conds) { | for (const Cond &cond : conds) { | 
	| 92 |  | if (!test(cond, accessor(cond.key))) { |  | 
	|  | 92 |  | bool matched = false; | 
	|  | 93 |  | for (const std::string &val : accessor(cond.key)) { | 
	|  | 94 |  | if (test(cond, val)) { | 
	|  | 95 |  | matched = true; | 
	|  | 96 |  | break; | 
	|  | 97 |  | } | 
	|  | 98 |  | } | 
	|  | 99 |  | if (!matched) { | 
	| 93 | 100 | err(cond); | err(cond); | 
	| 94 | 101 | } | } | 
	| 95 | 102 | } | } | 
| File src/ItemFilter.hpp changed (mode: 100644) (index d1f6836..b8c716b) | 
	| ... | ... | class Item; | 
	| 34 | 34 | */ | */ | 
	| 35 | 35 | class ItemFilter | class ItemFilter | 
	| 36 | 36 | { | { | 
	|  | 37 |  | /** | 
	|  | 38 |  | * @brief Type of function that is used to query field value. | 
	|  | 39 |  | * | 
	|  | 40 |  | * A field can be expanded to zero, one or multiple values and each value | 
	|  | 41 |  | * will be matched individually. | 
	|  | 42 |  | */ | 
	|  | 43 |  | using accessor_f = std::vector<std::string>(const std::string &key); | 
	|  | 44 |  |  | 
	| 37 | 45 | public: | public: | 
	| 38 | 46 | /** | /** | 
	| 39 | 47 | * @brief Constructs the filter out of conditions in textual form. | * @brief Constructs the filter out of conditions in textual form. | 
|  | 
	| ... | ... | public: | 
	| 72 | 80 | * | * | 
	| 73 | 81 | * @returns @c true if it passes, and @c false otherwise. | * @returns @c true if it passes, and @c false otherwise. | 
	| 74 | 82 | */ | */ | 
	| 75 |  | bool passes(std::function<std::string(const std::string &)> accessor) const; |  | 
	|  | 83 |  | bool passes(std::function<accessor_f> accessor) const; | 
	| 76 | 84 |  |  | 
	| 77 | 85 | /** | /** | 
	| 78 | 86 | * @brief Checks whether item represented by its fields passes the filter. | * @brief Checks whether item represented by its fields passes the filter. | 
|  | 
	| ... | ... | public: | 
	| 82 | 90 | * | * | 
	| 83 | 91 | * @returns @c true if it passes, and @c false otherwise. | * @returns @c true if it passes, and @c false otherwise. | 
	| 84 | 92 | */ | */ | 
	| 85 |  | bool passes(std::function<std::string(const std::string &)> accessor, |  | 
	| 86 |  | std::string &error) const; |  | 
	|  | 93 |  | bool passes(std::function<accessor_f> accessor, std::string &error) const; | 
	| 87 | 94 |  |  | 
	| 88 | 95 | private: | private: | 
	| 89 | 96 | /** | /** | 
| File src/cmds/AddCmd.cpp changed (mode: 100644) (index 0ee0412..ffdf63d) | 
	| ... | ... | AddCmd::run(Project &project, const std::vector<std::string> &args) | 
	| 125 | 125 |  |  | 
	| 126 | 126 | std::string guard = cfg.get("guards.newitem", std::string()); | std::string guard = cfg.get("guards.newitem", std::string()); | 
	| 127 | 127 | auto accessor = [&fields](const std::string &f) { | auto accessor = [&fields](const std::string &f) { | 
	|  | 128 |  | using return_t = std::vector<std::string>; | 
	| 128 | 129 | auto it = fields.find(f); | auto it = fields.find(f); | 
	| 129 |  | return (it == fields.cend()) ? std::string() : it->second; |  | 
	|  | 130 |  | return (it == fields.cend()) ? return_t{} : return_t{ it->second }; | 
	| 130 | 131 | }; | }; | 
	| 131 | 132 | std::string error; | std::string error; | 
	| 132 | 133 | if (!ItemFilter(breakIntoArgs(guard)).passes(accessor, error)) { | if (!ItemFilter(breakIntoArgs(guard)).passes(accessor, error)) { | 
| File tests/ItemFilter.cpp changed (mode: 100644) (index a180990..1c3b2f1) | 
	| ... | ... | TEST_CASE("Error messages add up", "[item-filter]") | 
	| 40 | 40 | ItemFilter filter({ "_id==notid", "title!=title", "_id!/ID", "title/xy" }); | ItemFilter filter({ "_id==notid", "title!=title", "_id!/ID", "title/xy" }); | 
	| 41 | 41 |  |  | 
	| 42 | 42 | std::string error; | std::string error; | 
	| 43 |  | auto accessor = [&item](const std::string &f) { return item.getValue(f); }; |  | 
	|  | 43 |  | auto accessor = [&item](const std::string &f) { | 
	|  | 44 |  | return std::vector<std::string>{ item.getValue(f) }; | 
	|  | 45 |  | }; | 
	| 44 | 46 | REQUIRE(!filter.passes(accessor, error)); | REQUIRE(!filter.passes(accessor, error)); | 
	| 45 | 47 |  |  | 
	| 46 | 48 | REQUIRE(split(error, '\n').size() == 4); | REQUIRE(split(error, '\n').size() == 4); |