1 module nxt.casing;
2 
3 import std.traits : isSomeString;
4 import nxt.string_traits : isASCIIString;
5 
6 version (unittest) {
7 	import std.algorithm : equal;
8 }
9 
10 /** Convert string $(S s) to lower-case.
11  *
12  * String must contain ASCII characters only.
13  */
14 auto toLowerASCII_(S)(S s)
15 if (isSomeString!S) {
16 	import std.algorithm.iteration : map;
17 	import std.ascii : toLower;
18 	import std.traits : isNarrowString;
19 	static if (isNarrowString!S) {
20 		import std.utf : byUTF;
21 		return s.byUTF!dchar.map!(ch => ch.toLower);
22 	}
23 	else
24 		return t.map!(ch => ch.toLower);
25 }
26 
27 /++ Return `s` lowercased if `s` is an ASCII-string, otherwise `defaultValue`. +/
28 string toLowerIfASCII(scope const(char)[] s, string defaultValue) @safe pure nothrow {
29 	if (!s.isASCIIString)
30 		return defaultValue;
31 	typeof(return) result;
32 	result.reserve(s.length);
33 	foreach (const c; s) {
34 		import std.ascii : toLower;
35 		result ~= c.toLower;
36 	}
37 	return result;
38 }
39 
40 /++ Return `s` lowercased if `s` is an ASCII-string, otherwise `s`. +/
41 inout(char)[] toLowerIfASCII(return scope inout(char)[] s) @safe pure nothrow {
42 	if (!s.isASCIIString)
43 		return s;
44 	typeof(return) result;
45 	result.reserve(s.length);
46 	foreach (const c; s) {
47 		import std.ascii : toLower;
48 		result ~= c.toLower;
49 	}
50 	return result;
51 }
52 
53 ///
54 @safe pure /*TODO: nothrow @nogc*/ unittest {
55 	assert("Lasse".toLowerIfASCII("Lasse").equal("lasse"));
56 	assert("Åberg".toLowerIfASCII("Åberg").equal("Åberg"));
57 	assert("Lasse".toLowerIfASCII.equal("lasse"));
58 	assert("Åberg".toLowerIfASCII.equal("Åberg"));
59 }
60 
61 /** Convert string $(S s) to lower-case.
62  *
63  * String may contain Unicode characters.
64  */
65 auto toLowerUnicode(S)(S s)
66 if (isSomeString!S) {
67 	import std.algorithm.iteration : map;
68 	import std.uni : toLower;
69 	import std.traits : isNarrowString;
70 	/+ TODO: functionize +/
71 	static if (isNarrowString!S) {
72 		import std.utf : byUTF;
73 		return s.byUTF!dchar.map!(ch => ch.toLower);
74 	}
75 	else
76 		return t.map!(ch => ch.toLower);
77 }
78 
79 ///
80 @safe pure /*TODO: nothrow @nogc*/ unittest {
81 	assert("Lasse".toLowerUnicode.equal("lasse"));
82 	assert("Åberg".toLowerUnicode.equal("åberg"));
83 }
84 
85 /** Convert D-style camel-cased string $(S s) to lower-cased words.
86  */
87 auto camelCasedToLower(S)(S s)
88 if (isSomeString!S) {
89 	import std.algorithm.iteration : map;
90 	import std.ascii : isUpper; // D symbol names can only be in ASCII
91 	/+ TODO: Instead of this add std.ascii.as[Lower|Upper]Case and import std.ascii.asLowerCase +/
92 	import std.uni : asLowerCase;
93 	import nxt.slicing : preSlicer;
94 	return s.preSlicer!isUpper.map!asLowerCase;
95 }
96 
97 ///
98 pure @safe unittest {
99 	auto x = "doThis".camelCasedToLower;
100 	assert(x.front.equal("do"));
101 	x.popFront();
102 	assert(x.front.equal("this"));
103 }
104 
105 /** Convert D-Style camel-cased string $(S s) to space-separated lower-cased words.
106  */
107 auto camelCasedToLowerSpaced(S, Separator)(S s, const Separator separator = " ")
108 if (isSomeString!S) {
109 	import std.algorithm.iteration : joiner;
110 	return camelCasedToLower(s).joiner(separator);
111 }
112 
113 ///
114 pure @safe unittest {
115 	assert(equal("doThis".camelCasedToLowerSpaced,
116 				 "do this"));
117 }
118 
119 /** Convert enumeration value (enumerator) $(D t) to a range chars.
120  */
121 auto toLowerSpacedChars(T, Separator)(const T t,
122 									  const Separator separator = " ")
123 if (is(T == enum)) {
124 	import nxt.enum_ex : toStringFromEnumWithConsecutiveAliases;
125 	return t.toStringFromEnumWithConsecutiveAliases
126 			.camelCasedToLowerSpaced(separator);
127 }
128 
129 ///
130 pure @safe unittest {
131 	enum Things { isUri, isLink }
132 	assert(Things.isUri.toLowerSpacedChars.equal("is uri"));
133 	assert(Things.isLink.toLowerSpacedChars.equal("is link"));
134 }
135 
136 ///
137 pure @safe unittest {
138 	enum Things { isURI, isLink }
139 	auto r = Things.isURI.toLowerSpacedChars;
140 	alias R = typeof(r);
141 	import std.range.primitives : ElementType;
142 	alias E = ElementType!R;
143 	static assert(is(E == dchar));
144 }