1 module nxt.capitalization; 2 3 import std.traits : isSomeString; 4 5 @safe: 6 7 /** Check if `s` is a lowercased ASCII string. */ 8 bool isLowercasedASCII(in char[] s) pure @safe nothrow @nogc { 9 import std.ascii : isLower; 10 foreach (const c; s) 11 if (!c.isLower) 12 return false; 13 return true; 14 } 15 16 /// ditto 17 pure @safe nothrow @nogc unittest { 18 assert(!`A`.isLowercasedASCII); 19 assert(`a`.isLowercasedASCII); 20 assert(`alpha`.isLowercasedASCII); 21 assert(!`ALPHA`.isLowercasedASCII); 22 assert(!`aThing`.isLowercasedASCII); 23 assert(!`Alpha`.isLowercasedASCII); 24 assert(!`Jack London`.isLowercasedASCII); 25 } 26 27 /** Check if `s` is an uppercased ASCII string. */ 28 bool isUppercasedASCII(in char[] s) pure @safe nothrow @nogc { 29 import std.ascii : isUpper; 30 foreach (const c; s) 31 if (!c.isUpper) 32 return false; 33 return true; 34 } 35 36 /// ditto 37 pure @safe nothrow @nogc unittest { 38 assert(`A`.isUppercasedASCII); 39 assert(!`a`.isUppercasedASCII); 40 assert(!`alpha`.isUppercasedASCII); 41 assert(`ALPHA`.isUppercasedASCII); 42 assert(!`aThing`.isUppercasedASCII); 43 assert(!`Alpha`.isUppercasedASCII); 44 assert(!`Jack London`.isUppercasedASCII); 45 } 46 47 /** Check if `s` starts with a capital letter followed by a lower letter. */ 48 bool isCapitalizedASCII(in char[] s) pure @safe nothrow @nogc { 49 import std.ascii : isUpper, isLower; 50 return (s.length >= 2 && 51 s[0].isUpper && 52 s[1].isLower); 53 } 54 55 /// ditto 56 pure @safe nothrow @nogc unittest { 57 assert(!`A`.isCapitalizedASCII); 58 assert(!`a`.isCapitalizedASCII); 59 assert(!`alpha`.isCapitalizedASCII); 60 assert(!`ALPHA`.isCapitalizedASCII); 61 assert(!`aThing`.isCapitalizedASCII); 62 assert(`Alpha`.isCapitalizedASCII); 63 assert(`Jack London`.isCapitalizedASCII); 64 } 65 66 /** Check if `s` starts with a capital letter followed by a lower letter. 67 */ 68 bool isCapitalizedSimple(S)(S s) if (isSomeString!S) { 69 import std.range.primitives : empty, front, popFront; 70 import std.uni : isUpper, isLower; 71 if (s.empty) { return false; } 72 const firstUpper = s.front.isUpper; 73 if (!firstUpper) return false; 74 s.popFront(); 75 if (s.empty) { return false; } 76 return s.front.isLower; 77 } 78 79 /// ditto 80 pure @safe unittest { 81 assert(!`A`.isCapitalizedSimple); 82 assert(!`a`.isCapitalizedSimple); 83 assert(!`alpha`.isCapitalizedSimple); 84 assert(!`ALPHA`.isCapitalizedSimple); 85 assert(!`aThing`.isCapitalizedSimple); 86 assert(`Alpha`.isCapitalizedSimple); 87 assert(`Jack London`.isCapitalizedSimple); 88 } 89 90 /** Check if `s` lowercased, that is only contains lower-case characters. 91 */ 92 bool isLowercased(S)(S s) if (isSomeString!S) { 93 import std.uni : isLower; 94 import std.algorithm.searching : all; 95 import std.traits : isNarrowString; 96 import std.utf : byUTF; 97 alias pred = isLower; 98 /+ TODO: functionize +/ 99 static if (isNarrowString!S) 100 return s.byUTF!dchar.all!(ch => pred(ch)); 101 else 102 return t.map!(ch => pred(ch)); 103 } 104 105 /// 106 pure @safe unittest { 107 assert(!`A`.isLowercased); 108 assert(`a`.isLowercased); 109 assert(!`Ä`.isLowercased); 110 assert(`ä`.isLowercased); 111 } 112 113 /** Check if `s` uppercased, that is only contains upper-case characters. 114 */ 115 bool isUppercased(S)(S s) if (isSomeString!S) { 116 import std.uni : isUpper; 117 import std.algorithm.searching : all; 118 import std.traits : isNarrowString; 119 import std.utf : byUTF; 120 alias pred = isUpper; 121 /+ TODO: functionize +/ 122 static if (isNarrowString!S) 123 return s.byUTF!dchar.all!(ch => pred(ch)); 124 else 125 return t.map!(ch => pred(ch)); 126 } 127 128 pure @safe unittest { 129 assert(`A`.isUppercased); 130 assert(!`a`.isUppercased); 131 assert(`Ä`.isUppercased); 132 assert(!`ä`.isUppercased); 133 } 134 135 /** Check if `s` has proper noun capitalization. 136 * 137 * That is, `s` starts with a capital letter followed by only lower letters. 138 */ 139 bool isCapitalized(S)(S s) if (isSomeString!S) { 140 import std.range.primitives : empty, front, popFront; 141 142 if (s.empty) { return false; } 143 144 import std.ascii : isDigit; 145 import std.uni : isUpper; 146 const firstDigit = s.front.isDigit; 147 const firstUpper = s.front.isUpper; 148 149 if (!(firstDigit || 150 firstUpper)) 151 return false; 152 153 s.popFront(); 154 155 if (s.empty) 156 return firstDigit; 157 else { 158 import std.uni : isLower; 159 import std.algorithm.searching : all; 160 return s.all!(x => (x.isDigit || 161 x.isLower)); 162 } 163 } 164 165 /// 166 pure @safe unittest { 167 assert(!``.isCapitalized); 168 assert(!`alpha`.isCapitalized); 169 assert(!`ALPHA`.isCapitalized); 170 assert(!`aThing`.isCapitalized); 171 assert(`Alpha`.isCapitalized); 172 assert(!`Jack London`.isCapitalized); 173 } 174 175 /** Return `true` if `s` has proper name capitalization, such as in 176 * "Africa" or "South Africa". 177 */ 178 bool isProperNameCapitalized(S)(S s) if (isSomeString!S) { 179 import nxt.splitter_ex : splitterASCII; 180 import std.algorithm.comparison : among; 181 import std.algorithm.searching : all; 182 import std.ascii : isWhite; 183 import std.uni : isUpper; 184 size_t index = 0; 185 foreach (const word; s.splitterASCII!(s => (s.isWhite || s == '-'))) { 186 const bool ok = ((index >= 1 && 187 (word.all!(word => word.isUpper) || // Henry II 188 word.among!(`of`, `upon`))) || 189 word.isCapitalized); 190 if (!ok) { return false; } 191 index += 1; 192 } 193 return true; 194 } 195 196 /// 197 pure @safe unittest { 198 assert(!`alpha`.isProperNameCapitalized); 199 assert(!`alpha centauri`.isProperNameCapitalized); 200 assert(!`ALPHA`.isProperNameCapitalized); 201 assert(!`ALPHA CENTAURI`.isProperNameCapitalized); 202 assert(!`aThing`.isProperNameCapitalized); 203 assert(`Alpha`.isProperNameCapitalized); 204 assert(`Alpha Centauri`.isProperNameCapitalized); 205 assert(`11104 Airion`.isProperNameCapitalized); 206 assert(`New York City`.isProperNameCapitalized); 207 assert(`1-Hexanol`.isProperNameCapitalized); 208 assert(`11-Hexanol`.isProperNameCapitalized); 209 assert(`22nd Army`.isProperNameCapitalized); 210 assert(!`22nd army`.isProperNameCapitalized); 211 assert(`2nd World War`.isProperNameCapitalized); 212 assert(`Second World War`.isProperNameCapitalized); 213 assert(`Värmland`.isProperNameCapitalized); 214 assert(!`The big sky`.isProperNameCapitalized); 215 assert(`Suur-London`.isProperNameCapitalized); 216 assert(`Kingdom of Sweden`.isProperNameCapitalized); 217 assert(`Stratford upon Avon`.isProperNameCapitalized); 218 assert(`Henry II`.isProperNameCapitalized); 219 }