1 /++ 2 + Various bits common to different parts of IRC, such as channel and user 3 + metadata. 4 +/ 5 module virc.common; 6 7 import std.datetime : SysTime; 8 import std.range : isOutputRange, put; 9 import std.typecons : Flag, Nullable; 10 11 import virc.modes; 12 import virc.usermask; 13 14 alias RFC2812Compliance = Flag!"RFC2812Compliance"; 15 /++ 16 + Metadata for an IRC channel. 17 +/ 18 struct Channel { 19 /// 20 string name; 21 void toString(T)(T sink) const if (isOutputRange!(T, const(char))) { 22 put(sink, name); 23 } 24 this(string str) @safe pure nothrow @nogc { 25 name = str; 26 } 27 this(string str, string modePrefixes, string chanPrefixes) @safe pure in { 28 import std.algorithm : canFind; 29 import std.array : front; 30 assert(str.length > 0); 31 assert(modePrefixes.canFind(str.front) || chanPrefixes.canFind(str.front)); 32 } do { 33 import std.algorithm : canFind; 34 import std.array : empty, front, popFront; 35 if (modePrefixes.canFind(str.front)) { 36 auto tmpCopy = str; 37 tmpCopy.popFront(); 38 if (!tmpCopy.empty && chanPrefixes.canFind(tmpCopy.front)) { 39 name = tmpCopy; 40 } else { 41 name = str; 42 } 43 } else { 44 name = str; 45 } 46 } 47 } 48 /// 49 @safe pure unittest { 50 assert(Channel("#test").name == "#test"); 51 assert(Channel("#test", "@%+", "#").name == "#test"); 52 assert(Channel("+test", "@%+", "+").name == "+test"); 53 assert(Channel("++test", "@%+", "+").name == "+test"); 54 assert(Channel("@+test", "@%+", "#+").name == "+test"); 55 assert(Channel("@+", "@%+", "#+").name == "+"); 56 assert(Channel("+", "@%+", "#+").name == "+"); 57 } 58 /++ 59 + Channel topic metadata. A message is guaranteed, but the time and user 60 + associated with setting it may not be present. 61 +/ 62 struct Topic { 63 /// 64 string message; 65 /// 66 Nullable!SysTime time; 67 /// 68 Nullable!User setBy; 69 auto opEquals(const Topic b) const { 70 return message == b.message; 71 } 72 /// 73 auto toHash() const { 74 return message.hashOf; 75 } 76 } 77 @system pure nothrow @nogc unittest { 78 assert(Topic("Hello!").toHash == Topic("Hello!").toHash); 79 } 80 /++ 81 + User metadata. User's mask will always be present, but real name's presence 82 + depends on the context. Account's presence depends on the server's 83 + capabilities. 84 +/ 85 struct User { 86 /// 87 UserMask mask; 88 /// 89 Nullable!string realName; 90 /// 91 Nullable!string account; 92 /// 93 this(string str) @safe pure nothrow @nogc { 94 mask = UserMask(str); 95 } 96 /// 97 this(UserMask mask_) @safe pure nothrow @nogc { 98 mask = mask_; 99 } 100 /// 101 this(string nick, string ident, string host) @safe pure nothrow @nogc { 102 mask.nickname = nick; 103 mask.ident = ident; 104 mask.host = host; 105 } 106 /// 107 auto nickname() const { 108 return mask.nickname; 109 } 110 /// 111 auto ident() const { 112 return mask.ident; 113 } 114 /// 115 auto host() const { 116 return mask.host; 117 } 118 /// 119 auto server() const { 120 assert(mask.ident.isNull); 121 assert(mask.host.isNull); 122 return mask.nickname; 123 } 124 void toString(T)(T sink) const if (isOutputRange!(T, const(char))) { 125 mask.toString(sink); 126 if (!account.isNull) { 127 put(sink, " ("); 128 put(sink, account.get); 129 put(sink, ")"); 130 } 131 } 132 auto opEquals(const User b) const { 133 if (!this.realName.isNull && !b.realName.isNull && (this.realName != b.realName)) { 134 return false; 135 } 136 if (!this.account.isNull && !b.account.isNull && (this.account != b.account)) { 137 return false; 138 } 139 return this.mask == b.mask; 140 } 141 /// 142 auto toHash() const { 143 return mask.hashOf; 144 } 145 } 146 @safe pure nothrow @nogc unittest { 147 auto user = User("Test!Testo@Testy"); 148 auto compUser = User("Test!Testo@Testy"); 149 assert(user.mask.nickname == "Test"); 150 assert(user.mask.ident == "Testo"); 151 assert(user.mask.host == "Testy"); 152 assert(user == compUser); 153 user.realName = "TestseT"; 154 compUser.realName = "whoever"; 155 assert(user != compUser); 156 compUser.realName = user.realName; 157 user.account = "Testeridoo"; 158 compUser.account = "Tototo"; 159 assert(user != compUser); 160 assert(User("Test!Testo@Testy") == User("Test", "Testo", "Testy")); 161 } 162 @safe pure unittest { 163 import std.conv : text; 164 auto user = User("Test!Testo@Testy"); 165 user.account = "Tester"; 166 assert(user.text == "Test!Testo@Testy (Tester)"); 167 } 168 @system pure nothrow /+@nogc+/ unittest { 169 immutable user = User("Test!Testo@Testy"); 170 immutable compUser = User("Test!Testo@Testy"); 171 assert(user.toHash == compUser.toHash); 172 }