1 module mail.headers;
2 
3 import std.conv;
4 import std.algorithm;
5 import std.string;
6 import std.array;
7 
8 debug import std.stdio;
9 
10 enum maxLen = 78;
11 
12 string norm(string)(string key)
13 {
14     return map!("a.capitalize()")(key.split("-")).join("-");
15 }
16 
17 struct Headers
18 {
19     private
20     {
21         struct Header
22         {
23             string key;
24             string value;
25         }
26 
27         Header[] _headers;
28     }
29 
30     this(in Headers rsh)
31     {
32         _headers ~= rsh._headers.idup;
33     }
34 
35     string[] all(string key) const pure
36     {
37         auto nKey = key.norm;
38         string[] result;
39         foreach (h; _headers)
40         {
41             if (h.key == nKey)
42             {
43                 result ~= h.value;
44             }
45         }
46         return result;
47     }
48 
49     void update(string[string] values) pure
50     {
51         foreach (k, ref v; values)
52         {
53             opIndexAssign(v, k);
54         }
55     }
56 
57     string opIndex(string key) pure const
58     {
59         auto nKey = key.norm;
60         auto val = "";
61         foreach (h; _headers)
62         {
63             if (h.key == nKey)
64             {
65                 val = h.value;
66                 break;
67             }
68         }
69         return val;
70     }
71 
72     string get(string key, lazy string defVal = null) pure const
73     {
74         auto nKey = key.norm;
75         foreach (h; _headers)
76         {
77             if (h.key == nKey)
78             {
79                 return h.value;
80             }
81         }
82         return defVal;
83     }
84 
85     void opIndexAssign(ulong val, string key) pure
86     {
87         opIndexAssign(to!string(val), key);
88     }
89 
90     void opIndexAssign(string[] val, string key) pure
91     {
92         opIndexAssign(val.join(","), key);
93     }
94 
95     void opIndexAssign(string val, string key) pure
96     {
97         auto nKey = key.norm;
98         foreach (h; _headers)
99         {
100             if (h.key == nKey)
101             {
102                 h.value = val;
103                 return;
104             }
105         }
106         _headers ~= Header(nKey, val);
107     }
108 
109     void add(string key, string val) pure
110     {
111         _headers ~= Header(key.norm, val);
112     }
113 
114     void toString(scope void delegate(const(char)[]) sink) const
115     {
116         foreach (item; _headers)
117         {
118             // TODO: Folding
119             sink(item.key);
120             sink(": ");
121             sink(item.value);
122             sink("\r\n");
123         }
124         sink("\r\n");
125     }
126 
127     void parse(string data) pure
128     {
129         string key;
130         string[] val;
131         void save()
132         {
133             add(key, val.join(" "));
134             val.length = 0;
135         }
136 
137         auto lines = data.split("\n");
138         while (lines.length)
139         {
140             auto tmp = lines.front.strip.findSplit(":");
141             key = tmp[0];
142             val ~= tmp[2].strip;
143             lines.popFront;
144             while (lines.length && (lines.front.startsWith("\t") || lines.front.startsWith(" ")))
145             {
146                 val ~= lines.front.strip;
147                 lines.popFront;
148             }
149             save();
150         }
151     }
152 }
153 
154 unittest
155 {
156     Headers h;
157 
158     h["key1"] = "val1";
159     h["key2"] = "val2";
160 
161     assert(h.get("key1") == "val1");
162     assert(h["key2"] == "val2");
163 
164     h.add("key2", "val2_2");
165     assert(h["key2"] == "val2");
166     assert(h.all("key2") == ["val2", "val2_2"]);
167 
168 }