1 /++
2 + Module for splitting IRC commands into its individual arguments for easier
3 + parsing.
4 +/
5 module virc.ircsplitter;
6 
7 import virc.common;
8 
9 ///
10 alias IRCSplitter = IRCSplitter_!(RFC2812Compliance.no);
11 ///
12 struct IRCSplitter_(RFC2812Compliance RFC2812) {
13 	private string str;
14 	private size_t upper;
15 	private bool colon;
16 	static if (RFC2812) {
17 		size_t position;
18 		size_t endOffset = 15;
19 	}
20 	///
21 	this(string input) @nogc @safe pure nothrow {
22 		str = input;
23 		popFront();
24 	}
25 	///
26 	void popFront() @nogc @safe pure nothrow {
27 		if (colon) {
28 			colon = false;
29 			return;
30 		}
31 		str = str[upper .. $];
32 		upper = 0;
33 		while ((str.length > 0) && str[0] == ' ') {
34 			str = str[1 .. $];
35 		}
36 		if (str.length == 0) {
37 			return;
38 		}
39 		if (str[0] == ':') {
40 			str = str[1 .. $];
41 			upper = str.length;
42 			if (upper == 0) {
43 				colon = true;
44 			}
45 		} else {
46 			foreach (i, char c; str) {
47 				if (c == ' ') {
48 					upper = i;
49 					break;
50 				}
51 			}
52 			if (upper == 0) {
53 				upper = str.length;
54 			}
55 		}
56 		static if (RFC2812) {
57 			position++;
58 			if (position == endOffset) {
59 				upper = str.length;
60 			}
61 		}
62 	}
63 	///
64 	auto empty() const {
65 		static if (RFC2812) {
66 			if (position > endOffset) {
67 				return true;
68 			}
69 		}
70 		return !colon && str.length == 0;
71 	}
72 	///
73 	auto front() const @nogc nothrow
74 		in(!empty)
75 	{
76 		return str[0..upper];
77 	}
78 	///
79 	auto save() {
80 		return this;
81 	}
82 }
83 ///
84 @safe pure nothrow @nogc unittest {
85 	import std.algorithm : equal;
86 	import std.range : only;
87 	assert(IRCSplitter("").empty);
88 	assert(IRCSplitter(" ").empty);
89 	assert(IRCSplitter("test").equal(only("test")));
90 	assert(IRCSplitter("test word2").equal(only("test", "word2")));
91 	assert(IRCSplitter("test  word2").equal(only("test", "word2")));
92 	assert(IRCSplitter("test  :word2").equal(only("test", "word2")));
93 	assert(IRCSplitter("test :word2 word3").equal(only("test", "word2 word3")));
94 	assert(IRCSplitter("test :word2 :word3").equal(only("test", "word2 :word3")));
95 	assert(IRCSplitter(":").equal(only("")));
96 	assert(IRCSplitter_!(RFC2812Compliance.yes)("word1 word2 word3 word4 word5 word6 word7 word8 word9 word10 word11 word12 word13 word14 word15 word16 word17").equal(only("word1", "word2", "word3", "word4", "word5", "word6", "word7", "word8", "word9", "word10", "word11", "word12", "word13", "word14", "word15 word16 word17")));
97 }