From 9f3a564d0dfc704f8d164cb5cb69fdd08deef52c Mon Sep 17 00:00:00 2001 From: Kevin Lee Date: Fri, 27 Oct 2023 18:50:42 +1100 Subject: [PATCH] Close #466 - [extras-string] Add case conversion - string.toSnakeLowerCase --- .../scala/extras/strings/syntax/cases.scala | 6 + .../ToSnakeLowerCaseSpec.scala | 280 ++++++++++++++++++ 2 files changed, 286 insertions(+) create mode 100644 modules/extras-string/shared/src/test/scala/extras/strings/syntax/casesSpec/stringCaseOpsSpec/ToSnakeLowerCaseSpec.scala diff --git a/modules/extras-string/shared/src/main/scala/extras/strings/syntax/cases.scala b/modules/extras-string/shared/src/main/scala/extras/strings/syntax/cases.scala index 88406611..e556cfd9 100644 --- a/modules/extras-string/shared/src/main/scala/extras/strings/syntax/cases.scala +++ b/modules/extras-string/shared/src/main/scala/extras/strings/syntax/cases.scala @@ -41,6 +41,12 @@ object cases extends cases { .map(_.toUpperCase(Locale.ENGLISH)) .mkString("_") + def toSnakeLowerCase: String = + s.split("[\\s_-]+") + .flatMap(_.splitByCase) + .map(_.toLowerCase(Locale.ENGLISH)) + .mkString("_") + @SuppressWarnings(Array("org.wartremover.warts.Equals")) def splitByCase: Vector[String] = { @scala.annotation.tailrec diff --git a/modules/extras-string/shared/src/test/scala/extras/strings/syntax/casesSpec/stringCaseOpsSpec/ToSnakeLowerCaseSpec.scala b/modules/extras-string/shared/src/test/scala/extras/strings/syntax/casesSpec/stringCaseOpsSpec/ToSnakeLowerCaseSpec.scala new file mode 100644 index 00000000..5e60c9a4 --- /dev/null +++ b/modules/extras-string/shared/src/test/scala/extras/strings/syntax/casesSpec/stringCaseOpsSpec/ToSnakeLowerCaseSpec.scala @@ -0,0 +1,280 @@ +package extras.strings.syntax.casesSpec.stringCaseOpsSpec + +import extras.strings.syntax.casesSpec.Gens +import hedgehog._ +import hedgehog.runner._ + +import java.util.Locale + +/** @author Kevin Lee + * @since 2023-10-26 + */ +object ToSnakeLowerCaseSpec extends Properties { + // String.toSnakeLowerCase + + import extras.strings.syntax.cases._ + + def tests: List[Test] = List( + property( + "test String.toCamelCase with already snake_case", + testToSnakeLowerCaseWithAlreadySnakeCase, + ), + property("test String.toSnakeLowerCase with PascalCases", testToSnakeLowerCaseWithPascalCases), + property("test String.toSnakeLowerCase with camelCases", testToSnakeLowerCaseWithCamelCases), + property("test String.toSnakeLowerCase with all UPPERCASE", testToSnakeLowerCaseWithUpperCase), + property("test String.toSnakeLowerCase with all lowercase", testToSnakeLowerCaseWithLowerCase), + property("test String.toSnakeLowerCase with all Snake_Cases", testToSnakeLowerCaseWithSnakeCases), + property("test String.toSnakeLowerCase with all lower_snake_cases", testToSnakeLowerCaseWithLowerSnakeCases), + property("test String.toSnakeLowerCase with all UPPER_SNAKE_CASES", testToSnakeLowerCaseWithUpperSnakeCases), + property("test String.toSnakeLowerCase with all Kebab-Cases", testToSnakeLowerCaseWithKebabCases), + property("test String.toSnakeLowerCase with all lower-kebab-cases", testToSnakeLowerCaseWithLowerKebabCases), + property("test String.toSnakeLowerCase with all UPPER-KEBAB-CASES", testToSnakeLowerCaseWithUpperKebabCases), + property( + "test String.toSnakeLowerCase with all space separated String values", + testToSnakeLowerCaseWithSpaceSeparatedStringValues, + ), + property( + "test String.toSnakeLowerCase with all lower space separated String values", + testToSnakeLowerCaseWithLowerSpaceSeparatedStringValues, + ), + property( + "test String.toSnakeLowerCase with all UPPER SPACE SEPARATED STRING VALUES", + testToSnakeLowerCaseWithUpperSpaceSeparatedStringValues, + ), + ) + + def testToSnakeLowerCaseWithAlreadySnakeCase: Property = for { + input <- Gen + .string(Gen.lower, Range.linear(1, 10)) + .list(Range.linear(1, 10)) + .map(_.mkString("_")) + .log("input") + expected <- Gen.constant(input.map(_.toLower)).log("expected") + } yield { + val actual = input.toSnakeLowerCase + actual ==== expected + } + + def testToSnakeLowerCaseWithPascalCases: Property = for { + ss <- Gens.genPascalCase(2, 10).list(Range.linear(1, 4)).log("ss") + input <- Gen.constant(ss.mkString).log("input") + } yield { + val expected = ss.map(_.toLowerCase(Locale.ENGLISH)).mkString("_") + val actual = input.toSnakeLowerCase + + val info = + s""" + |> input: $input + |> actual: $actual + |> expected: $expected + |""".stripMargin + + (actual ==== expected).log(info) + } + + def testToSnakeLowerCaseWithCamelCases: Property = for { + s <- Gen.string(Gen.lower, Range.linear(1, 10)).log("s") + ss <- Gens.genPascalCase(2, 10).list(Range.linear(1, 4)).log("ss") + input <- Gen.constant(s + ss.mkString).log("input") + } yield { + val expected = (s :: ss).map(_.toLowerCase(Locale.ENGLISH)).mkString("_") + val actual = input.toSnakeLowerCase + + val info = + s""" + |> input: $input + |> actual: $actual + |> expected: $expected + |""".stripMargin + + (actual ==== expected).log(info) + } + + def testToSnakeLowerCaseWithUpperCase: Property = for { + expected <- Gen.string(Gen.lower, Range.linear(1, 10)).log("expected") + input <- Gen.constant(expected.toUpperCase(Locale.ENGLISH)).log("input") + } yield { + val actual = input.toSnakeLowerCase + + val info = + s""" + |> input: $input + |> actual: $actual + |> expected: $expected + |""".stripMargin + + (actual ==== expected).log(info) + } + + def testToSnakeLowerCaseWithLowerCase: Property = for { + input <- Gen.string(Gen.lower, Range.linear(1, 10)).log("input") + expected <- Gen.constant(input).log("expected") + } yield { + val actual = input.toSnakeLowerCase + + val info = + s""" + |> input: $input + |> actual: $actual + |> expected: $expected + |""".stripMargin + + (actual ==== expected).log(info) + } + + def testToSnakeLowerCaseWithSnakeCases: Property = for { + ss <- Gens.genPascalCase(1, 10).list(Range.linear(1, 5)).log("ss") + input <- Gen.constant(ss.mkString("_")).log("input") + } yield { + val expected = ss.map(_.toLowerCase(Locale.ENGLISH)).mkString("_") + val actual = input.toSnakeLowerCase + + val info = + s""" + |> input: $input + |> actual: $actual + |> expected: $expected + |""".stripMargin + + (actual ==== expected).log(info) + } + + def testToSnakeLowerCaseWithLowerSnakeCases: Property = for { + ss <- Gen.string(Gen.lower, Range.linear(1, 10)).list(Range.linear(1, 5)).log("ss") + input <- Gen.constant(ss.mkString("_")).log("input") + } yield { + val expected = ss.mkString("_") + val actual = input.toSnakeLowerCase + + val info = + s""" + |> input: $input + |> actual: $actual + |> expected: $expected + |""".stripMargin + + (actual ==== expected).log(info) + } + + def testToSnakeLowerCaseWithUpperSnakeCases: Property = for { + ss <- Gen.string(Gen.upper, Range.linear(1, 10)).list(Range.linear(1, 5)).log("ss") + input <- Gen.constant(ss.mkString("_")).log("input") + } yield { + val expected = ss.map(_.toLowerCase(Locale.ENGLISH)).mkString("_") + val actual = input.toSnakeLowerCase + + val info = + s""" + |> input: $input + |> actual: $actual + |> expected: $expected + |""".stripMargin + + (actual ==== expected).log(info) + } + + def testToSnakeLowerCaseWithKebabCases: Property = for { + ss <- Gens.genPascalCase(1, 10).list(Range.linear(1, 5)).log("ss") + input <- Gen.constant(ss.mkString("-")).log("input") + } yield { + val expected = ss.map(_.toLowerCase(Locale.ENGLISH)).mkString("_") + val actual = input.toSnakeLowerCase + + val info = + s""" + |> input: $input + |> actual: $actual + |> expected: $expected + |""".stripMargin + + (actual ==== expected).log(info) + } + + def testToSnakeLowerCaseWithLowerKebabCases: Property = for { + ss <- Gen.string(Gen.lower, Range.linear(1, 10)).list(Range.linear(1, 5)).log("ss") + input <- Gen.constant(ss.mkString("-")).log("input") + } yield { + val expected = ss.mkString("_") + val actual = input.toSnakeLowerCase + + val info = + s""" + |> input: $input + |> actual: $actual + |> expected: $expected + |""".stripMargin + + (actual ==== expected).log(info) + } + + def testToSnakeLowerCaseWithUpperKebabCases: Property = for { + ss <- Gen.string(Gen.upper, Range.linear(1, 10)).list(Range.linear(1, 5)).log("ss") + input <- Gen.constant(ss.mkString("-")).log("input") + } yield { + val expected = ss.map(_.toLowerCase(Locale.ENGLISH)).mkString("_") + val actual = input.toSnakeLowerCase + + val info = + s""" + |> input: $input + |> actual: $actual + |> expected: $expected + |""".stripMargin + + (actual ==== expected).log(info) + } + + def testToSnakeLowerCaseWithSpaceSeparatedStringValues: Property = for { + s <- Gens.genPascalCase(1, 10).log("ss") + ss <- Gens.genPascalCase(2, 10).list(Range.linear(1, 4)).log("ss") + input <- Gen.constant((s :: ss).mkString(" ")).log("input") + } yield { + val expected = (s :: ss).map(_.toLowerCase(Locale.ENGLISH)).mkString("_") + val actual = input.toSnakeLowerCase + + val info = + s""" + |> input: $input + |> actual: $actual + |> expected: $expected + |""".stripMargin + + (actual ==== expected).log(info) + } + + def testToSnakeLowerCaseWithLowerSpaceSeparatedStringValues: Property = for { + s <- Gen.string(Gen.lower, Range.linear(1, 10)).log("s") + ss <- Gen.string(Gen.lower, Range.linear(2, 10)).list(Range.linear(1, 5)).log("ss") + input <- Gen.constant((s :: ss).mkString(" ")).log("input") + } yield { + val expected = (s :: ss).mkString("_") + val actual = input.toSnakeLowerCase + + val info = + s""" + |> input: $input + |> actual: $actual + |> expected: $expected + |""".stripMargin + + (actual ==== expected).log(info) + } + + def testToSnakeLowerCaseWithUpperSpaceSeparatedStringValues: Property = for { + s <- Gen.string(Gen.upper, Range.linear(1, 10)).log("s") + ss <- Gen.string(Gen.upper, Range.linear(2, 10)).list(Range.linear(1, 5)).log("ss") + input <- Gen.constant((s :: ss).mkString(" ")).log("input") + } yield { + val expected = (s :: ss).map(_.toLowerCase(Locale.ENGLISH)).mkString("_") + val actual = input.toSnakeLowerCase + + val info = + s""" + |> input: $input + |> actual: $actual + |> expected: $expected + |""".stripMargin + + (actual ==== expected).log(info) + } +}