1 module dast.util;
2 
3 version (Have_database_util) {
4 	public import database.util : as, ignore, snakeCase, camelCase, KeyName;
5 } else {
6 	struct as { // @suppress(dscanner.style.phobos_naming_convention)
7 		string name;
8 	}
9 
10 	enum ignore; // @suppress(dscanner.style.phobos_naming_convention)
11 
12 	import tame.text.ascii;
13 
14 	S snakeCase(S)(S input, char sep = '_') {
15 		if (!input.length)
16 			return [];
17 		char[128] buffer;
18 		size_t length;
19 
20 		auto pcls = classify(input[0]);
21 		foreach (ch; input) {
22 			auto cls = classify(ch);
23 			switch (cls) with (CharClass) {
24 			case UpperCase:
25 				if (pcls != UpperCase && pcls != Underscore)
26 					buffer[length++] = sep;
27 				buffer[length++] = ch | ' ';
28 				break;
29 			case Digit:
30 				if (pcls != Digit)
31 					buffer[length++] = sep;
32 				goto default;
33 			default:
34 				buffer[length++] = ch;
35 				break;
36 			}
37 			pcls = cls;
38 
39 			if (length >= buffer.length - 1)
40 				break;
41 		}
42 		return cast(S)buffer[0 .. length].dup;
43 	}
44 
45 	unittest {
46 		static void test(string str, string expected) {
47 			auto result = str.snakeCase;
48 			assert(result == expected, str ~ ": " ~ result);
49 		}
50 
51 		test("AA", "aa");
52 		test("AaA", "aa_a");
53 		test("AaA1", "aa_a_1");
54 		test("AaA11", "aa_a_11");
55 		test("_AaA1", "_aa_a_1");
56 		test("_AaA11_", "_aa_a_11_");
57 		test("aaA", "aa_a");
58 		test("aaAA", "aa_aa");
59 		test("aaAA1", "aa_aa_1");
60 		test("aaAA11", "aa_aa_11");
61 		test("authorName", "author_name");
62 		test("authorBio", "author_bio");
63 		test("authorPortraitId", "author_portrait_id");
64 		test("authorPortraitID", "author_portrait_id");
65 		test("coverURL", "cover_url");
66 		test("coverImageURL", "cover_image_url");
67 	}
68 
69 	S camelCase(S, bool upper = false)(S input, char sep = '_') {
70 		S output;
71 		bool upcaseNext = upper;
72 		foreach (c; input) {
73 			if (c != sep) {
74 				if (upcaseNext) {
75 					output ~= c.toUpper;
76 					upcaseNext = false;
77 				} else
78 					output ~= c.toLower;
79 			} else
80 				upcaseNext = true;
81 		}
82 		return output;
83 	}
84 
85 	unittest {
86 		assert("c".camelCase == "c");
87 		assert("c".camelCase!true == "C");
88 		assert("c_a".camelCase == "cA");
89 		assert("ca".camelCase!true == "Ca");
90 		assert("camel".camelCase!true == "Camel");
91 		assert("Camel".camelCase!false == "camel");
92 		assert("camel_case".camelCase!true == "CamelCase");
93 		assert("camel_camel_case".camelCase!true == "CamelCamelCase");
94 		assert("caMel_caMel_caSe".camelCase!true == "CamelCamelCase");
95 		assert("camel2_camel2_case".camelCase!true == "Camel2Camel2Case");
96 		assert("get_http_response_code".camelCase == "getHttpResponseCode");
97 		assert("get2_http_response_code".camelCase == "get2HttpResponseCode");
98 		assert("http_response_code".camelCase!true == "HttpResponseCode");
99 		assert("http_response_code_xyz".camelCase!true == "HttpResponseCodeXyz");
100 	}
101 
102 	/// Get the keyname of `T`
103 	template KeyName(alias T, string defaultName = T.stringof) {
104 		import std.traits;
105 
106 		static if (hasUDA!(T, ignore))
107 			enum KeyName = "";
108 		else static if (hasUDA!(T, as))
109 			enum KeyName = getUDAs!(T, as)[0].name;
110 		else
111 			static foreach (attr; __traits(getAttributes, T))
112 			static if (is(typeof(KeyName) == void) && is(typeof(attr(""))))
113 				enum KeyName = attr(defaultName);
114 		static if (is(typeof(KeyName) == void))
115 			enum KeyName = defaultName;
116 	}
117 
118 	alias SQLName = KeyName;
119 }