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