From d823721fb2b332e2ad735a1fae7cfa043594ee21 Mon Sep 17 00:00:00 2001
From: Emmanuel Benoit <tseeker@nocternity.net>
Date: Tue, 8 May 2018 13:58:39 +0200
Subject: [PATCH] Strings - Character mapping, conversion to upper/lowercase

---
 include/ebcl/Strings.hh | 12 ++++++++++++
 src/Strings.cc          | 27 +++++++++++++++++++++++++++
 tests/strings.cc        | 39 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 78 insertions(+)

diff --git a/include/ebcl/Strings.hh b/include/ebcl/Strings.hh
index 83f32da..cd35cef 100644
--- a/include/ebcl/Strings.hh
+++ b/include/ebcl/Strings.hh
@@ -65,6 +65,8 @@ double UTF8ToDouble( char const* input , uint32_t size ,
 
 struct T_Character
 {
+	using F_Map = std::function< T_Character( T_Character ) >;
+
 	const uint32_t codepoint;
 
 	T_Character( ) noexcept;
@@ -240,6 +242,16 @@ class T_String
 
 	// ---------------------------------------------------------------------
 
+	// Generate a string using a function that transforms characters
+	T_String mapped( T_Character::F_Map f ) const noexcept;
+
+	// Convert the string to uppercase
+	T_String toUpper( ) const noexcept;
+	// Convert the string to lowercase
+	T_String toLower( ) const noexcept;
+
+	// ---------------------------------------------------------------------
+
 	bool equals( T_String const& other ) const;
 	bool equals( char const* string ) const;
 
diff --git a/src/Strings.cc b/src/Strings.cc
index 905cfc0..3e1168f 100644
--- a/src/Strings.cc
+++ b/src/Strings.cc
@@ -1447,6 +1447,33 @@ T_String T_String::trim( ) const noexcept
 
 /*----------------------------------------------------------------------------*/
 
+T_String T_String::mapped( T_Character::F_Map f ) const noexcept
+{
+	T_StringIterator it{ *this };
+	T_StringBuilder sb;
+	while ( !it.atEnd( ) ) {
+		sb << f( it );
+		it.next( );
+	}
+	return std::move( sb );
+}
+
+T_String T_String::toUpper( ) const noexcept
+{
+	return mapped( [](auto c){
+			return c.toUpper( );
+		} );
+}
+
+T_String T_String::toLower( ) const noexcept
+{
+	return mapped( [](auto c){
+			return c.toLower( );
+		} );
+}
+
+/*----------------------------------------------------------------------------*/
+
 int32_t T_String::compare( T_String const& other ) const
 {
 	if ( this == &other || data_ == other.data_ ) {
diff --git a/tests/strings.cc b/tests/strings.cc
index ee8086c..f6a446b 100644
--- a/tests/strings.cc
+++ b/tests/strings.cc
@@ -126,6 +126,10 @@ class StringsTest : public CppUnit::TestFixture
 		CPPUNIT_TEST( testToSignedLeadingWhitespaceOnly );
 
 		CPPUNIT_TEST( testToDouble );
+
+		CPPUNIT_TEST( testMapped );
+		CPPUNIT_TEST( testToUpper );
+		CPPUNIT_TEST( testToLower );
 	CPPUNIT_TEST_SUITE_END( );
 
 public:
@@ -249,6 +253,10 @@ public:
 	void testToSignedLeadingWhitespaceOnly( );
 
 	void testToDouble( );
+
+	void testMapped( );
+	void testToUpper( );
+	void testToLower( );
 };
 CPPUNIT_TEST_SUITE_REGISTRATION( StringsTest );
 
@@ -1182,3 +1190,34 @@ void StringsTest::testToDouble( )
 }
 
 #undef M_CHECK_
+
+/*----------------------------------------------------------------------------*/
+
+void StringsTest::testMapped( )
+{
+	const T_String test{ "thisisatest" };
+	const T_String expected{ "uijtjtbuftu" };
+	const T_String result{ test.mapped( [](auto c){
+			return c + 1;
+		} ) };
+	CPPUNIT_ASSERT_EQUAL( test.length( ) , result.length( ) );
+	CPPUNIT_ASSERT( result.equals( expected ) );
+}
+
+void StringsTest::testToUpper( )
+{
+	const T_String test{ "This IS a test, 123" };
+	const T_String expected{ "THIS IS A TEST, 123" };
+	const T_String result{ test.toUpper( ) };
+	CPPUNIT_ASSERT_EQUAL( test.length( ) , result.length( ) );
+	CPPUNIT_ASSERT( result.equals( expected ) );
+}
+
+void StringsTest::testToLower( )
+{
+	const T_String test{ "This IS a test, 123" };
+	const T_String expected{ "this is a test, 123" };
+	const T_String result{ test.toLower( ) };
+	CPPUNIT_ASSERT_EQUAL( test.length( ) , result.length( ) );
+	CPPUNIT_ASSERT( result.equals( expected ) );
+}