Add String conversion functions snake_case -> PascalCase and vice versa.

This commit is contained in:
Christoffer Lerno
2025-07-23 00:26:44 +02:00
parent 5f0a7dd63e
commit 9575698fa4
3 changed files with 142 additions and 0 deletions

View File

@@ -1,5 +1,7 @@
module std::core::string;
import std::io;
import std::core::mem::allocator;
typedef String @if(!$defined(String)) = inline char[];
<*
@@ -775,6 +777,92 @@ fn String String.to_upper_copy(self, Allocator allocator)
return copy;
}
fn String String.capitalize_copy(self, Allocator allocator)
{
String s = self.copy(allocator);
if (s.len > 0 && s[0].is_lower())
{
s[0] &= (char)~0x20;
}
return s;
}
<*
Convert a string from `snake_case` to PascalCase.
@param [in] self
@return `"FooBar" from "foo_bar" the resulting pointer may safely be cast to ZString.`
*>
fn String String.snake_to_pascal_copy(self, Allocator allocator)
{
Splitter splitter = self.tokenize("_");
char[] new_string = allocator::alloc_array(allocator, char, self.len + 1);
usz index = 0;
while (try s = splitter.next())
{
assert(s.len > 0);
char c = s[0];
if (c.is_lower()) c = c.to_upper();
new_string[index++] = c;
s = s[1..];
new_string[index:s.len] = s[..];
index += s.len;
}
new_string[index] = 0;
return (String)new_string[:index];
}
<*
Movifies the current string from `snake_case` to PascalCase.
@param [inout] self
*>
fn void String.convert_snake_to_pascal(&self)
{
Splitter splitter = self.tokenize("_");
String new_string = *self;
usz index = 0;
while (try s = splitter.next())
{
assert(s.len > 0);
char c = s[0];
if (c.is_lower()) c = c.to_upper();
new_string[index++] = c;
s = s[1..];
new_string[index:s.len] = s[..];
index += s.len;
}
*self = new_string[:index];
}
<*
Convert a string from `PascalCase` to `snake_case`.
@param [in] self
@return `"foo_bar" from "FooBar" the resulting pointer may safely be cast to ZString.`
*>
fn String String.pascal_to_snake_copy(self, Allocator allocator) => @pool()
{
DString d;
d.init(tmem, (usz)(self.len * 1.5));
usz index = 0;
foreach (i, c : self)
{
if (c.is_upper())
{
if (i > 0 && ((self[i - 1].is_lower() || self[i - 1].is_digit()) || (i < self.len - 1 && self[i + 1].is_lower())))
{
d.append_char('_');
}
d.append_char(c.to_lower());
continue;
}
d.append_char(c);
}
return d.copy_str(allocator);
}
fn StringIterator String.iterator(self)
{
return { self, 0 };

View File

@@ -87,6 +87,7 @@
- Added `Ref` and `RefCounted` experimental functionality.
- Added `Volatile` generic type.
- Added `UnalignedRef` generic type.
- Add String conversion functions snake_case -> PascalCase and vice versa.
## 0.7.3 Change list

View File

@@ -311,4 +311,57 @@ fn void test_float()
{
test::eq_approx(- 2.04632e-05," - 2.04632e-05 ".to_float()!!, delta: 0.00001e-5);
test::eq_approx(2.04632e-05," + 2.04632e-05 ".to_float()!!, delta: 0.00001e-5);
}
fn void test_pascal_to_snake()
{
String[2][*] test_cases = {
{ "HTTPRequest2Handler", "http_request2_handler" },
{ "MyJSON2XMLConverter", "my_json2_xml_converter"},
{ "Foo3C3CBazHello", "foo3_c3_c_baz_hello" },
{ "TLAWithABCs", "tla_with_ab_cs" },
{ "HTMLToPDFConverter", "html_to_pdf_converter"},
{ "OAuth2Token", "o_auth2_token" },
{ "startMIDDLELast", "start_middle_last" }
};
foreach (s : test_cases)
{
test::eq(s[0].pascal_to_snake_copy(tmem), s[1]);
}
}
fn void test_snake_pascal()
{
String[2][*] test_cases = {
{ "http_request2_handler", "HttpRequest2Handler", },
{ "my_json2_xml_converter", "MyJson2XmlConverter", },
{ "foo3_c3_c_baz_hello", "Foo3C3CBazHello", },
{ "tla_with_ab_cs", "TlaWithAbCs", },
{ "html_to_pdf_converter", "HtmlToPdfConverter", },
{ "o_auth2_token", "OAuth2Token", },
{ "start_middle_last", "StartMiddleLast", }
};
foreach (s : test_cases)
{
test::eq(s[0].snake_to_pascal_copy(tmem), s[1]);
}
}
fn void test_snake_pascal_self_modify()
{
String[2][*] test_cases = {
{ "http_request2_handler", "HttpRequest2Handler", },
{ "my_json2_xml_converter", "MyJson2XmlConverter", },
{ "foo3_c3_c_baz_hello", "Foo3C3CBazHello", },
{ "tla_with_ab_cs", "TlaWithAbCs", },
{ "html_to_pdf_converter", "HtmlToPdfConverter", },
{ "o_auth2_token", "OAuth2Token", },
{ "start_middle_last", "StartMiddleLast", }
};
foreach (s : test_cases)
{
String s2 = s[0].tcopy();
s2.convert_snake_to_pascal();
test::eq(s2, s[1]);
}
}