1+ /*
2+ * (C) Copyright 1996- ECMWF.
3+ *
4+ * This software is licensed under the terms of the Apache Licence Version 2.0
5+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6+ * In applying this licence, ECMWF does not waive the privileges and immunities
7+ * granted to it by virtue of its status as an intergovernmental organisation nor
8+ * does it submit to any jurisdiction.
9+ */
10+
11+ // / @author Chris Bradley
12+
13+ #include < string>
14+ #include < map>
15+
16+ #include " eckit/utils/Regex.h"
17+ #include " eckit/utils/Tokenizer.h"
18+ #include " eckit/exception/Exceptions.h"
19+ #include " metkit/mars/MarsRequest.h"
20+
21+ #include " metkit/mars/Matcher.h"
22+
23+ namespace metkit ::mars {
24+
25+ namespace { // helpers
26+
27+ using RegexMap = std::map<std::string, eckit::Regex>;
28+ using ValuesMap = std::map<std::string, std::vector<std::string>>;
29+
30+ RegexMap parseKeyRegexList (const std::string& expr) {
31+ RegexMap out;
32+ if (expr.empty ()) {
33+ return out;
34+ }
35+
36+ std::vector<std::string> key_vals;
37+ eckit::Tokenizer (' ,' )(expr, key_vals);
38+
39+ eckit::Tokenizer equals (' =' );
40+ for (const std::string& item : key_vals) {
41+ std::vector<std::string> kv;
42+ equals (item, kv);
43+ if (kv.size () != 2 ) {
44+ throw eckit::UserError (" Invalid condition " + item + " in expression: " + expr, Here ());
45+ }
46+
47+ const std::string& key = kv[0 ];
48+ const std::string& val = kv[1 ];
49+
50+ if (out.find (key) != out.end ()) {
51+ throw eckit::UserError (" Duplicate key " + key + " in expression: " + expr, Here ());
52+ }
53+
54+ out[key] = eckit::Regex (val);
55+ }
56+ return out;
57+ }
58+
59+ } // namespace helpers
60+
61+ Matcher::Matcher (const std::string& expr):
62+ regexMap_{parseKeyRegexList (expr)} {
63+ }
64+
65+ bool Matcher::match (const MarsRequest& request, bool matchOnMissing, ValuePolicy policy) const {
66+ ValuesMap vals;
67+ for (const auto & kv : regexMap_) {
68+ vals[kv.first ] = request.values (kv.first , /* emptyOK*/ true );
69+ }
70+ return match (vals, matchOnMissing, policy);
71+ }
72+
73+ bool Matcher::match (const ValuesMap& request, bool matchOnMissing, ValuePolicy policy) const {
74+ return std::all_of (regexMap_.begin (), regexMap_.end (),
75+ [&](const auto & kv) {
76+ const auto & keyword = kv.first ;
77+ const auto & re = kv.second ;
78+
79+ auto it = request.find (keyword);
80+
81+ // If the key is missing or present with no values
82+ if (it == request.end () || it->second .empty ())
83+ return matchOnMissing;
84+
85+ const auto & vals = it->second ;
86+ auto pred = [&](const std::string& s){ return re.match (s); };
87+
88+ if (policy == ValuePolicy::Any)
89+ return std::any_of (vals.begin (), vals.end (), pred);
90+ else if (policy == ValuePolicy::All)
91+ return std::all_of (vals.begin (), vals.end (), pred);
92+ else
93+ throw eckit::SeriousBug (" Not implemented ValuePolicy in Matcher::match" );
94+ });
95+ }
96+
97+ } // namespace metkit::mars
0 commit comments