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 }