1 module jcli.core.pattern;
2 
3 struct Pattern
4 {
5     import std.ascii;
6     import std.algorithm;
7 
8     string[] items;
9     alias items this;
10     
11     this(string[] items) @safe pure nothrow
12     in
13     {
14         assert(items.length > 0, "The pattern must contain at least one item.");
15         assert(items.all!(i => i.all!isASCII), "The pattern items must be ascii.");
16         assert(items.all!(i => i.length > 0), "The pattern must not contain empty items.");
17         assert(items.map!(i => i.length).maxIndex == 0, "The first item in the items list must be the longest");
18     }
19     do
20     {
21         this.items = items;
22     }
23     
24     static Pattern parse(string patternString) @safe pure 
25     {
26         import std.string : split;
27         
28         auto items = patternString.split('|');
29         return Pattern(items);
30     }
31 
32     @safe // TODO: when and how does it allocate tho? @nogc
33     auto matches(bool caseInsensitive)(string input) pure nothrow
34     {
35         return items.filter!((p) {
36             import std.algorithm : map, equal;
37 
38             static if (caseInsensitive)
39             {
40                 import std.ascii : toLower;
41                 if (p.length != input.length)
42                     return false;
43                 foreach (index; 0 .. p.length)
44                 {
45                     if (toLower(p[index]) != toLower(input[index]))
46                         return false;
47                 }
48                 return true;
49             }
50             else
51                 return p == input;
52         });
53     }
54     ///
55     unittest
56     {
57         import std.algorithm : equal;
58         auto p = Pattern.parse("a|A");
59         {
60             enum caseInsensitive = true;
61             assert(equal(p.matches!(caseInsensitive)("a"), ["a", "A"]));
62             assert(equal(p.matches!(caseInsensitive)("b"), string[].init));
63         }
64         {
65             enum caseInsensitive = false;
66             assert(equal(p.matches!(caseInsensitive)("a"), ["a"]));
67             assert(equal(p.matches!(caseInsensitive)("A"), ["A"]));
68             assert(equal(p.matches!(caseInsensitive)("b"), string[].init));
69         }
70     }
71     // @safe @nogc
72     // string firstMatch(bool caseInsensitive)(string input) nothrow pure
73     // {
74     //     static struct Result
75     //     {
76     //         bool matched;
77     //         string pattern;
78     //     }
79     //     auto m = matches!caseInsensitive(input);
80     //     if (m.empty)
81     //         return Result(false, null);
82     //     return Result(true, m.front);
83     // }
84 }