1 module virc.target; 2 /++ 3 + 4 +/ 5 struct Target { 6 import std.range : isOutputRange; 7 import std.typecons : Nullable; 8 import virc.common : Channel, User; 9 /// 10 Nullable!Channel channel; 11 /// 12 Nullable!User user; 13 /// 14 bool isChannel() @safe pure nothrow @nogc const { 15 return !channel.isNull; 16 } 17 /// 18 bool isUser() @safe pure nothrow @nogc const { 19 return !user.isNull; 20 } 21 /++ 22 + Mode prefixes present in target. May be present on both channels and users. 23 + 24 + Channel mode prefixes are used when targetting a subset of users on that 25 + channel (all voiced users, for example), while user mode prefixes are found 26 + mainly in responses from the server. 27 +/ 28 Nullable!string prefixes; 29 /// 30 bool isNickname() @safe pure nothrow @nogc const { 31 return !user.isNull; 32 } 33 bool opEquals(string target) @safe pure nothrow @nogc const { 34 if (!channel.isNull) { 35 return channel.get == Channel(target); 36 } else if (!user.isNull) { 37 return user.get == User(target); 38 } 39 return false; 40 } 41 bool opEquals(const User target) @safe pure nothrow @nogc const { 42 if (!user.isNull) { 43 return user.get == target; 44 } 45 return false; 46 } 47 bool opEquals(const Channel target) @safe pure nothrow @nogc const { 48 if (!channel.isNull) { 49 return channel.get == target; 50 } 51 return false; 52 } 53 void toString(T)(T sink) const if (isOutputRange!(T, const(char))) { 54 if (!channel.isNull) { 55 channel.get.toString(sink); 56 } else if (!user.isNull) { 57 user.get.toString(sink); 58 } 59 } 60 /++ 61 + 62 +/ 63 this(Channel chan) @safe pure nothrow @nogc { 64 channel = chan; 65 } 66 /++ 67 + 68 +/ 69 this(User user_) @safe pure nothrow @nogc { 70 user = user_; 71 } 72 this(string str, string modePrefixes, string channelPrefixes) @safe pure nothrow { 73 import std.array : empty, front, popFront; 74 import std.algorithm : canFind; 75 import std.utf : byDchar; 76 if (str.empty) { 77 return; 78 } 79 auto tmpStr = str; 80 while (modePrefixes.byDchar.canFind(tmpStr.byDchar.front)) { 81 if (prefixes.isNull) { 82 prefixes = ""; 83 } 84 prefixes.get ~= tmpStr.byDchar.front; 85 tmpStr.popFront(); 86 } 87 if (!tmpStr.empty && channelPrefixes.byDchar.canFind(tmpStr.byDchar.front)) { 88 channel = Channel(tmpStr); 89 } else { 90 user = User(tmpStr); 91 } 92 } 93 /++ 94 + 95 +/ 96 auto targetText() const { 97 if (!channel.isNull) { 98 return channel.get.name; 99 } else if (!user.isNull) { 100 return user.get.nickname; 101 } 102 assert(0, "No target specified"); 103 } 104 } 105 /// 106 @safe pure nothrow unittest { 107 import virc.common : Channel, User; 108 { 109 Target target; 110 target.channel = Channel("#hello"); 111 assert(target == Channel("#hello")); 112 assert(target != User("test")); 113 assert(target == "#hello"); 114 assert(target.isChannel && !target.isUser); 115 } 116 { 117 Target target; 118 target.user = User("test"); 119 assert(target != Channel("#hello")); 120 assert(target == User("test")); 121 assert(target == "test"); 122 assert(!target.isChannel && target.isUser); 123 } 124 { 125 Target target; 126 assert(target != Channel("#hello")); 127 assert(target != User("test")); 128 assert(target != "test"); 129 assert(target != "#hello"); 130 } 131 assert(Target("Hello", "+@%", "#&") == User("Hello")); 132 assert(Target(Channel("#test")) == Channel("#test")); 133 assert(Target(User("Test")) == User("Test")); 134 { 135 auto target = Target("+Hello", "+@%", "#&"); 136 assert(target == User("Hello")); 137 assert(target.prefixes == "+"); 138 } 139 assert(Target("#Hello", "+@%", "#&") == Channel("#Hello")); 140 { 141 auto target = Target("+#Hello", "+@%", "#&"); 142 assert(target == Channel("#Hello")); 143 assert(target.prefixes == "+"); 144 } 145 { 146 auto target = Target("+@#Hello", "+@%", "#&"); 147 assert(target == Channel("#Hello")); 148 assert(target.prefixes == "+@"); 149 } 150 { 151 auto target = Target("", "", ""); 152 assert(target.channel.isNull); 153 assert(target.user.isNull); 154 } 155 }