1 /++
2 + IRC user masks.
3 +/
4 module virc.usermask;
5 
6 import std.algorithm : findSplit;
7 import std.range;
8 import std.typecons : Nullable;
9 
10 /++
11 + IRC user masks are generally in the form nick!ident@hostname. This struct
12 + exists for easy separation and manipulation of each piece of the mask. This
13 + also accepts cases where the ident and host are not present.
14 +/
15 struct UserMask {
16 	///
17 	string nickname;
18 	///
19 	Nullable!string ident;
20 	///
21 	Nullable!string host;
22 	///
23 	this(string maskString) @safe pure nothrow @nogc {
24 		auto split = maskString.findSplit("!");
25 		nickname = split[0];
26 		if ((split[1] == "!") && (split[2].length > 0)) {
27 			auto split2 = split[2].findSplit("@");
28 			ident = split2[0];
29 			if (split2[1] == "@") {
30 				host = split2[2];
31 			}
32 		} else {
33 			auto split2 = maskString.findSplit("@");
34 			nickname = split2[0];
35 			if (split2[1] == "@") {
36 				host = split2[2];
37 			}
38 		}
39 	}
40 	void toString(T)(T sink) const if (isOutputRange!(T, const(char))) {
41 		put(sink, nickname);
42 		if (!ident.isNull) {
43 			put(sink, '!');
44 			put(sink, ident.get);
45 			assert(!host.isNull);
46 		}
47 		if (!host.isNull) {
48 			put(sink, '@');
49 			put(sink, host.get);
50 		}
51 	}
52 	auto toHash() const {
53 		auto hash = nickname.hashOf();
54 		if (!ident.isNull) {
55 			hash = ident.get.hashOf(hash);
56 		}
57 		if (!host.isNull) {
58 			hash = host.get.hashOf(hash);
59 		}
60 		return hash;
61 	}
62 }
63 
64 @safe pure nothrow @nogc unittest {
65 	with (UserMask("localhost")) {
66 		assert(nickname == "localhost");
67 		assert(ident.isNull);
68 		assert(host.isNull);
69 	}
70 	with (UserMask("user!id@example.net")) {
71 		assert(nickname == "user");
72 		assert(ident.get == "id");
73 		assert(host.get == "example.net");
74 	}
75 	with (UserMask("user!id@example!!!.net")) {
76 		assert(nickname == "user");
77 		assert(ident.get == "id");
78 		assert(host.get == "example!!!.net");
79 	}
80 	with (UserMask("user!id@ex@mple!!!.net")) {
81 		assert(nickname == "user");
82 		assert(ident.get == "id");
83 		assert(host.get == "ex@mple!!!.net");
84 	}
85 	with (UserMask("user!id!@ex@mple!!!.net")) {
86 		assert(nickname == "user");
87 		assert(ident.get == "id!");
88 		assert(host.get == "ex@mple!!!.net");
89 	}
90 	with (UserMask("user!id")) {
91 		assert(nickname == "user");
92 		assert(ident.get == "id");
93 		assert(host.isNull);
94 	}
95 	with (UserMask("user@example.net")) {
96 		assert(nickname == "user");
97 		assert(ident.isNull);
98 		assert(host.get == "example.net");
99 	}
100 }
101 @safe pure unittest {
102 	import std.conv : text;
103 	{
104 		UserMask mask;
105 		mask.nickname = "Nick";
106 		assert(mask.text == "Nick");
107 	}
108 	{
109 		UserMask mask;
110 		mask.nickname = "Nick";
111 		mask.ident = "user";
112 		mask.host = "domain";
113 		assert(mask.text == "Nick!user@domain");
114 	}
115 }