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 }