1 /++
2 + Numerics for METADATA specification (post-3.2 WIP)
3 +/
4 module virc.numerics.metadata;
5 import virc.numerics.definitions;
6 
7 struct RPL_WhoisKeyValue {
8 	import virc.target : Target;
9 	Target target;
10 	string key;
11 	string visibility;
12 	string value;
13 }
14 /++
15 +
16 + Format is `760 <Target> <Key> <Visibility> :<Value>`
17 +/
18 auto parseNumeric(Numeric numeric : Numeric.RPL_WHOISKEYVALUE, T)(T input, string prefixes, string channelTypes) {
19 	import std.typecons : Nullable;
20 	import virc.numerics.magicparser : autoParse;
21 	import virc.target : Target;
22 	struct Reduced {
23 		string target;
24 		string key;
25 		string visibility;
26 		string value;
27 	}
28 	Nullable!RPL_WhoisKeyValue output;
29 	auto reply = autoParse!Reduced(input);
30 	if (!reply.isNull) {
31 		output = RPL_WhoisKeyValue(Target(reply.get.target, prefixes, channelTypes), reply.get.key, reply.get.visibility, reply.get.value);
32 	}
33 	return output;
34 }
35 ///
36 @safe pure nothrow unittest { //Numeric.RPL_WHOISKEYVALUE
37 	import virc.common : User;
38 	import std.range : only, takeNone;
39 	{
40 		with(parseNumeric!(Numeric.RPL_WHOISKEYVALUE)(only("someone!test@example.com", "url", "*", "http://www.example.com"), "@", "#").get) {
41 			assert(target.user == User("someone!test@example.com"));
42 			assert(key == "url");
43 			assert(visibility == "*");
44 			assert(value == "http://www.example.com");
45 		}
46 	}
47 	{
48 		immutable logon = parseNumeric!(Numeric.RPL_WHOISKEYVALUE)(takeNone(only("")), "@", "#");
49 		assert(logon.isNull);
50 	}
51 	{
52 		immutable logon = parseNumeric!(Numeric.RPL_WHOISKEYVALUE)(only("*"), "@", "#");
53 		assert(logon.isNull);
54 	}
55 	{
56 		immutable logon = parseNumeric!(Numeric.RPL_WHOISKEYVALUE)(only("*", "url"), "@", "#");
57 		assert(logon.isNull);
58 	}
59 	{
60 		immutable logon = parseNumeric!(Numeric.RPL_WHOISKEYVALUE)(only("*", "url", "*"), "@", "#");
61 		assert(logon.isNull);
62 	}
63 }
64 
65 struct RPL_KeyValue {
66 	import virc.numerics.magicparser : Optional;
67 	import virc.target : Target;
68 	import std.typecons : Nullable;
69 	Target target;
70 	string key;
71 	string visibility;
72 	@Optional Nullable!string value;
73 }
74 /++
75 +
76 + Format is `761 <Target> <Key> <Visibility>[ :<Value>]`
77 +/
78 auto parseNumeric(Numeric numeric : Numeric.RPL_KEYVALUE, T)(T input, string prefixes, string channelTypes) {
79 	import std.typecons : Nullable;
80 	import virc.numerics.magicparser : autoParse, Optional;
81 	import virc.target : Target;
82 	struct Reduced {
83 		string target;
84 		string key;
85 		string visibility;
86 		@Optional Nullable!string value;
87 	}
88 	Nullable!RPL_KeyValue output;
89 	auto reply = autoParse!Reduced(input);
90 	if (!reply.isNull) {
91 		output = RPL_KeyValue(Target(reply.get.target, prefixes, channelTypes), reply.get.key, reply.get.visibility, reply.get.value);
92 	}
93 	return output;
94 }
95 ///
96 @safe pure nothrow unittest { //Numeric.RPL_KEYVALUE
97 	import std.range : only, takeNone;
98 	import virc.common : User;
99 
100 	with(parseNumeric!(Numeric.RPL_KEYVALUE)(only("someone!test@example.com", "url", "*", "http://www.example.com"), "@", "#").get) {
101 		assert(target== User("someone!test@example.com"));
102 		assert(key == "url");
103 		assert(visibility == "*");
104 		assert(value == "http://www.example.com");
105 	}
106 
107 	assert(parseNumeric!(Numeric.RPL_KEYVALUE)(takeNone(only("")), "@", "#").isNull);
108 	assert(parseNumeric!(Numeric.RPL_KEYVALUE)(only("*"), "@", "#").isNull);
109 	assert(parseNumeric!(Numeric.RPL_KEYVALUE)(only("*", "url"), "@", "#").isNull);
110 
111 	with(parseNumeric!(Numeric.RPL_KEYVALUE)(only("*", "url", "*"), "@", "#").get) {
112 		assert(target == "*");
113 		assert(key == "url");
114 		assert(visibility == "*");
115 		assert(value.isNull);
116 	}
117 }
118 
119 struct RPL_KeyNotSet {
120 	import virc.target : Target;
121 	Target target;
122 	string key;
123 	string humanReadable;
124 }
125 /++
126 +
127 + Format is `766 <Target> <Key> :no matching key`
128 +/
129 auto parseNumeric(Numeric numeric : Numeric.RPL_KEYNOTSET, T)(T input, string prefixes, string channelTypes) {
130 	import std.typecons : Nullable;
131 	import virc.numerics.magicparser : autoParse;
132 	import virc.target : Target;
133 	struct Reduced {
134 		string target;
135 		string key;
136 		string errorMessage;
137 	}
138 	Nullable!RPL_KeyNotSet output;
139 	auto reply = autoParse!Reduced(input);
140 	if (!reply.isNull) {
141 		output = RPL_KeyNotSet(Target(reply.get.target, prefixes, channelTypes), reply.get.key, reply.get.errorMessage);
142 	}
143 	return output;
144 }
145 ///
146 @safe pure nothrow unittest { //Numeric.RPL_KEYNOTSET
147 	import std.range : only, takeNone;
148 	import virc.common : User;
149 
150 	with(parseNumeric!(Numeric.RPL_KEYNOTSET)(only("someone!test@example.com", "examplekey", "no matching key"), "@", "#").get) {
151 		assert(target== User("someone!test@example.com"));
152 		assert(key== "examplekey");
153 		assert(humanReadable == "no matching key");
154 	}
155 
156 	assert(parseNumeric!(Numeric.RPL_KEYNOTSET)(takeNone(only("")), "@", "#").isNull);
157 	assert(parseNumeric!(Numeric.RPL_KEYNOTSET)(only("someone!test@example.com"), "@", "#").isNull);
158 	assert(parseNumeric!(Numeric.RPL_KEYNOTSET)(only("someone!test@example.com", "examplekey"), "@", "#").isNull);
159 }
160 
161 /++
162 +
163 + Format is `770 :<Key1> [<Key2> ...]` OR
164 + `771 :<Key1> [<Key2> ...]`
165 + `772 :<Key1> [<Key2> ...]`
166 +/
167 auto parseNumeric(Numeric numeric, T)(T input) if ((numeric == Numeric.RPL_METADATASUBOK) || (numeric == Numeric.RPL_METADATAUNSUBOK) || (numeric == Numeric.RPL_METADATASUBS)) {
168 	import std.algorithm.iteration : splitter;
169 	import std.typecons : Nullable, Tuple;
170 	import virc.numerics.magicparser : autoParse;
171 	static struct Reduced {
172 		string target;
173 		string subs;
174 	}
175 	Nullable!(Tuple!(typeof("".splitter(" ")), "subs")) output = Tuple!(typeof("".splitter(" ")), "subs")();
176 	auto reply = autoParse!Reduced(input);
177 	if (!reply.isNull) {
178 		output = typeof(output.get).init;
179 		output.get.subs = reply.get.subs.splitter(" ");
180 		return output;
181 	} else {
182 		return output.init;
183 	}
184 }
185 ///
186 @safe pure nothrow unittest { //Numeric.RPL_METADATASUBOK, Numeric.RPL_METADATAUNSUBOK, Numeric.RPL_METADATASUBS
187 	import std.array : array;
188 	import std.range : only, takeNone;
189 	import virc.common : User;
190 
191 	with(parseNumeric!(Numeric.RPL_METADATASUBOK)(only("client", "url example")).get) {
192 		assert(subs.array == ["url", "example"]);
193 	}
194 	with(parseNumeric!(Numeric.RPL_METADATAUNSUBOK)(only("client", "url example")).get) {
195 		assert(subs.array == ["url", "example"]);
196 	}
197 	with(parseNumeric!(Numeric.RPL_METADATASUBS)(only("client", "url example")).get) {
198 		assert(subs.array == ["url", "example"]);
199 	}
200 
201 	assert(parseNumeric!(Numeric.RPL_METADATASUBOK)(takeNone(only(""))).isNull);
202 	assert(parseNumeric!(Numeric.RPL_METADATAUNSUBOK)(takeNone(only(""))).isNull);
203 	assert(parseNumeric!(Numeric.RPL_METADATASUBS)(takeNone(only(""))).isNull);
204 }
205 
206 struct ERR_MetadataSyncLater {
207 	import core.time : Duration;
208 	import std.typecons : Nullable;
209 	import virc.target : Target;
210 	Target target;
211 	Nullable!Duration retryAfter;
212 }
213 /++
214 +
215 + Format is `774 <Target>[ <RetryAfter>]`
216 +/
217 auto parseNumeric(Numeric numeric : Numeric.ERR_METADATASYNCLATER, T)(T input, string prefixes, string channelTypes) {
218 	import core.time : Duration;
219 	import std.typecons : Nullable;
220 	import virc.numerics.magicparser : autoParse, Optional;
221 	import virc.target : Target;
222 	struct Reduced {
223 		string target;
224 		@Optional Duration time;
225 	}
226 	Nullable!ERR_MetadataSyncLater output;
227 	auto reply = autoParse!Reduced(input);
228 	if (!reply.isNull) {
229 		output = ERR_MetadataSyncLater(Target(reply.get.target, prefixes, channelTypes), Nullable!Duration(reply.get.time));
230 	}
231 	return output;
232 }