1 /** 2 * Copyright: Copyright (c) 2017 Wojciech Szęszoł. All rights reserved. 3 * Authors: Wojciech Szęszoł 4 * Version: Initial created: October 02, 2017 5 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 */ 7 module dstep.translator.ConvertCase; 8 9 auto splitSpelling(string spelling) 10 { 11 import std.algorithm; 12 import std.ascii; 13 import std.range; 14 15 struct Range 16 { 17 private string spelling; 18 private string next; 19 private bool capitals; 20 21 this(string spelling) 22 { 23 this.spelling = spelling.stripLeft('_'); 24 capitals = !spelling.canFind!isLower; 25 popFront(); 26 } 27 28 bool empty() const 29 { 30 return next.empty; 31 } 32 33 string front() const 34 { 35 return next; 36 } 37 38 void popFront() 39 { 40 if (spelling.empty) 41 { 42 next = spelling; 43 } 44 else 45 { 46 size_t end = capitals 47 ? spelling[1 .. $].countUntil!(a => a == '_') 48 : spelling[1 .. $].countUntil!( 49 a => !a.isLower && !a.isDigit); 50 51 end = end == -1 ? spelling.length : end + 1; 52 next = spelling[0 .. end]; 53 spelling = spelling[end .. $].stripLeft('_'); 54 } 55 } 56 } 57 58 return Range(spelling); 59 } 60 61 unittest 62 { 63 import std.array; 64 65 assert(splitSpelling("").array == []); 66 assert(splitSpelling("__").array == []); 67 assert(splitSpelling("x").array == ["x"]); 68 assert(splitSpelling("X").array == ["X"]); 69 assert(splitSpelling("xxx").array == ["xxx"]); 70 assert(splitSpelling("xxxXxx").array == ["xxx", "Xxx"]); 71 assert(splitSpelling("xxxXxxYyy").array == ["xxx", "Xxx", "Yyy"]); 72 assert(splitSpelling("xxxXxxYYY").array == ["xxx", "Xxx", "Y", "Y", "Y"]); 73 assert(splitSpelling("xxx_Xxx__YYY").array == ["xxx", "Xxx", "Y", "Y", "Y"]); 74 assert(splitSpelling("__xxx_Xxx__YYY__").array == ["xxx", "Xxx", "Y", "Y", "Y"]); 75 assert(splitSpelling("YYY").array == ["YYY"]); 76 assert(splitSpelling("Y_Y__XXY").array == ["Y", "Y", "XXY"]); 77 assert(splitSpelling("__Y_Y__XXY__").array == ["Y", "Y", "XXY"]); 78 79 assert(splitSpelling("xyz0").array == ["xyz0"]); 80 } 81 82 string toCamelCase(string spelling) 83 { 84 import std.algorithm; 85 import std.array; 86 import std.ascii; 87 import std.math; 88 89 auto split = spelling.splitSpelling(); 90 91 if (split.empty) 92 { 93 return string.init; 94 } 95 else 96 { 97 char[] camelCase(string x) 98 { 99 return (cast(char) x[0].toUpper) ~ 100 x[1 .. $].map!(x => cast(char) x.toLower).array; 101 } 102 103 char[] front; 104 105 while (split.front.length == 1) 106 { 107 front ~= cast(char) split.front.front.toLower; 108 split.popFront(); 109 } 110 111 if (front.empty) 112 { 113 front = split.front.map!(x => cast(char) x.toLower).array; 114 split.popFront(); 115 } 116 117 return (front ~ split.map!camelCase.join).idup; 118 } 119 } 120 121 unittest 122 { 123 import std.array; 124 125 assert("".toCamelCase() == ""); 126 assert("x".toCamelCase() == "x"); 127 assert("X".toCamelCase() == "x"); 128 assert("xy".toCamelCase() == "xy"); 129 assert("xy_zw".toCamelCase() == "xyZw"); 130 assert("xyZw".toCamelCase() == "xyZw"); 131 assert("XY_ZW".toCamelCase() == "xyZw"); 132 assert("XXXy_ZW".toCamelCase() == "xxXyZW"); 133 assert("XXy_ZW".toCamelCase() == "xXyZW"); 134 } 135 136 string toSnakeCase(string spelling) 137 { 138 import std.algorithm; 139 import std.range; 140 import std.uni; 141 import std.utf; 142 143 auto split = spelling.splitSpelling(); 144 145 if (split.empty) 146 { 147 return string.init; 148 } 149 else 150 { 151 char[] front; 152 153 while (split.front.length == 1) 154 { 155 front ~= cast(char) split.front.front.toLower; 156 split.popFront(); 157 } 158 159 if (front.empty) 160 { 161 front = split.front.dup; 162 split.popFront(); 163 } 164 165 return chain(only(front), split).join('_').map!toLower.toUTF8(); 166 } 167 } 168 169 unittest 170 { 171 import std.array; 172 173 assert("".toSnakeCase() == ""); 174 assert("x".toSnakeCase() == "x"); 175 assert("X".toSnakeCase() == "x"); 176 assert("xy".toSnakeCase() == "xy"); 177 assert("xy_zw".toSnakeCase() == "xy_zw"); 178 assert("xyZw".toSnakeCase() == "xy_zw"); 179 assert("XY_ZW".toSnakeCase() == "xy_zw"); 180 assert("XXXy_ZW".toSnakeCase() == "xx_xy_z_w"); 181 assert("XXy_ZW".toSnakeCase() == "x_xy_z_w"); 182 }