1 module tagion.hibon.HiBONregex;
2 
3 @safe:
4 import std.algorithm;
5 import std.range;
6 import std.regex;
7 import tagion.basic.basic;
8 import tagion.hibon.Document;
9 import tagion.hibon.HiBONBase;
10 import tagion.hibon.HiBONRecord;
11 
12 struct HiBONregex {
13     alias RegexT = Regex!char;
14     uint[] types;
15     string name;
16     string record_type;
17     RegexT regex_name;
18     RegexT regex_record_type;
19     this(Name, HType, Types)(Name name, HType record_type, Types types) pure
20 
21     
22 
23             if ((is(Name == string) || is(Name == RegexT)) &&
24                 (is(HType == string) || is(HType == RegexT)) &&
25                 (is(Types
26                 : const(Type))) || is(Types : const(Type[]))) {
27         static if (is(Name == string)) {
28             this.name = name;
29         }
30         else {
31             regex_name = name;
32         }
33         static if (is(HType == string)) {
34             this.record_type = record_type;
35         }
36         else {
37             regex_record_type = record_type;
38         }
39         import std.algorithm : filter;
40         import std.array;
41 
42         Type[] _types;
43         _types ~= types;
44         this.types = _types
45             .filter!(type => type !is Type.NONE)
46             .map!(type => cast(uint) type)
47             .array;
48     }
49 
50     this(Name)(Name name) pure {
51         this(name, string.init, Type.init);
52     }
53 
54     this(Name, HType)(Name name, HType record_type) pure {
55         this(name, record_type, Type.init);
56     }
57 
58     bool match(const Document doc) const {
59         bool result;
60         if (!record_type.empty) {
61             if (record_type == "!") {
62                 return doc.getType.isinit;
63             }
64             if (doc.getType != record_type) {
65                 return false;
66             }
67         }
68         else if (!regex_record_type.isinit) {
69             if (doc.getType.matchFirst(regex_record_type).empty) {
70                 return false;
71             }
72         }
73         bool canMatch(const Document.Element elm) {
74             if (!name.empty) {
75                 if (elm.key != name) {
76                     return false;
77                 }
78             }
79             else if (!regex_name.isinit) {
80                 if (elm.key.matchFirst(regex_name).empty) {
81                     return false;
82                 }
83             }
84             if (!types.empty) {
85                 if (!types.canFind(elm.type)) {
86                     return false;
87                 }
88             }
89             return true;
90         }
91 
92         return doc[].any!(elm => canMatch(elm));
93     }
94 
95     bool match(T)(T rec) if (isHiBONRecord!T) {
96         return match(rec.toDoc);
97     }
98 }
99 
100 ///
101 unittest {
102     enum record_type_name = "Some_type";
103     @recordType(record_type_name)
104     static struct RegexDoc {
105         string name;
106         int x;
107         @label("12") bool flag;
108         mixin HiBONRecord!(q{
109             this(string name, int x, bool flag) {
110                 this.name=name;
111                 this.x=x;
112                 this.flag=flag;
113             }
114         });
115     }
116 
117     static struct NoRecordType {
118         int x;
119         mixin HiBONRecord;
120     }
121 
122     const no_record_type_doc = NoRecordType.init.toDoc;
123     const regex_doc = RegexDoc("text", 42, false);
124     const doc = regex_doc.toDoc;
125     { // Test hibon key name match
126         assert(HiBONregex("name").match(doc));
127         assert(HiBONregex(regex(`\d+`)).match(doc));
128         assert(!HiBONregex("no match").match(doc));
129         assert(!HiBONregex(regex(`no match`)).match(doc));
130 
131     }
132     { // Test hibon record type match
133         assert(HiBONregex(string.init, record_type_name).match(doc));
134         assert(!HiBONregex(string.init, "Not found").match(doc));
135         assert(HiBONregex(string.init, regex(`_\w+`)).match(doc));
136         assert(!HiBONregex(string.init, regex(`_\d+`)).match(doc));
137         assert(!HiBONregex(string.init, "!").match(doc));
138         assert(HiBONregex(string.init, "!").match(no_record_type_doc));
139     }
140     { // Test hibon record type match
141         assert(HiBONregex(string.init, string.init, Type.STRING).match(doc));
142         assert(!HiBONregex(string.init, string.init, Type.BINARY).match(doc));
143         assert(HiBONregex(string.init, string.init, [Type.BINARY, Type.STRING]).match(doc));
144         assert(!HiBONregex(string.init, string.init, [Type.BINARY, Type.BIGINT]).match(doc));
145     }
146 }