1 /** 2 * snake_case transform 3 * 4 * Copyright: © 2016 David Monagle 5 * License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. 6 * Authors: David Monagle 7 */ 8 module transforms.snake; 9 10 import std.uni; 11 import std.algorithm; 12 import std.regex; 13 14 /// Transforms the given `input` into snake_case 15 /// This precomiled regex version does not work at compile time as regex uses malloc 16 string snakeCase(const string input) { 17 static auto firstCapRE = ctRegex!(`(.)([A-Z][a-z]+)`); 18 static auto allCapRE = ctRegex!(`([a-z0-9])([A-Z])`, "g"); 19 20 string output = input.replace(firstCapRE, `$1_$2`); 21 output = output.replace(allCapRE, `$1_$2`); 22 23 return output.toLower; 24 } 25 26 unittest { 27 assert("C".snakeCase == "c"); 28 assert("cA".snakeCase == "c_a"); 29 assert("Ca".snakeCase == "ca"); 30 assert("Camel".snakeCase == "camel"); 31 assert("CamelCase".snakeCase == "camel_case"); 32 assert("CamelCamelCase".snakeCase == "camel_camel_case"); 33 assert("Camel2Camel2Case".snakeCase == "camel2_camel2_case"); 34 assert("getHTTPResponseCode".snakeCase == "get_http_response_code"); 35 assert("getHttpResponseCode".snakeCase == "get_http_response_code"); 36 assert("get2HTTPResponseCode".snakeCase == "get2_http_response_code"); 37 assert("HTTPResponseCode".snakeCase == "http_response_code"); 38 assert("HTTPResponseCodeXYZ".snakeCase == "http_response_code_xyz"); 39 } 40 41 /// Transforms the given `input` into snake_case 42 /// Works at compile time 43 string snakeCaseCT(const string input) { 44 string firstPass(const string input) { 45 if (input.length < 3) return input; 46 47 string output; 48 for(auto index = 2; index < input.length; index++) { 49 output ~= input[index - 2]; 50 if (input[index - 1].isUpper && input[index].isLower) 51 output ~= "_"; 52 } 53 54 return output ~ input[$-2..$]; 55 } 56 57 string secondPass(const string input) { 58 if (input.length < 2) return input; 59 60 string output; 61 for(auto index = 1; index < input.length; index++) { 62 output ~= input[index - 1]; 63 if (input[index].isUpper && (input[index-1].isLower || input[index-1].isNumber)) 64 output ~= "_"; 65 } 66 67 return output ~ input[$-1..$]; 68 } 69 70 if (input.length < 2) return input.toLower; 71 72 string output = firstPass(input); 73 output = secondPass(output); 74 75 return output.toLower; 76 } 77 78 79 unittest { 80 assert("C".snakeCaseCT == "c"); 81 assert("cA".snakeCaseCT == "c_a"); 82 assert("Ca".snakeCaseCT == "ca"); 83 assert("Camel".snakeCaseCT == "camel"); 84 assert("CamelCase".snakeCaseCT == "camel_case"); 85 assert("CamelCamelCase".snakeCaseCT == "camel_camel_case"); 86 assert("Camel2Camel2Case".snakeCaseCT == "camel2_camel2_case"); 87 assert("getHTTPResponseCode".snakeCaseCT == "get_http_response_code"); 88 assert("get2HTTPResponseCode".snakeCaseCT == "get2_http_response_code"); 89 assert("HTTPResponseCode".snakeCaseCT == "http_response_code"); 90 assert("HTTPResponseCodeXYZ".snakeCaseCT == "http_response_code_xyz"); 91 }