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 }