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