diff --git a/enumeratum-core/src/main/scala/enumeratum/Enum.scala b/enumeratum-core/src/main/scala/enumeratum/Enum.scala index a92766d9..b1283f90 100644 --- a/enumeratum-core/src/main/scala/enumeratum/Enum.scala +++ b/enumeratum-core/src/main/scala/enumeratum/Enum.scala @@ -59,11 +59,29 @@ trait Enum[A] { */ lazy final val namesToValuesMap: Map[String, A] = values map (v => v.toString -> v) toMap + /** + * Map of [[A]] object names in lower case to [[A]]s for case-insensitive comparison + */ + lazy final val lowerCaseNamesToValuesMap: Map[String, A] = values map (v => v.toString.toLowerCase -> v) toMap + + /** + * Optionally returns an [[A]] for a given name. + */ + def withNameOption(name: String): Option[A] = namesToValuesMap get name + + /** + * Optionally returns an [[A]] for a given name, disregarding case + */ + def withNameInsensitiveOption(name: String): Option[A] = lowerCaseNamesToValuesMap get name.toLowerCase + /** * Tries to get an [[A]] by the supplied name. The name corresponds to the .toString * of the case objects implementing [[A]] + * + * Like [[Enumeration]]'s `withName`, this method will throw if the name does not match any of the values' + * .toString names. */ def withName(name: String): A = - namesToValuesMap getOrElse (name, throw new IllegalArgumentException(s"$name is not a member of Enum $this")) + withNameOption(name) getOrElse (throw new NoSuchElementException(s"$name is not a member of Enum $this")) } \ No newline at end of file diff --git a/enumeratum-core/src/test/scala/enumeratum/EnumSpec.scala b/enumeratum-core/src/test/scala/enumeratum/EnumSpec.scala index 00ae1a73..66508038 100644 --- a/enumeratum-core/src/test/scala/enumeratum/EnumSpec.scala +++ b/enumeratum-core/src/test/scala/enumeratum/EnumSpec.scala @@ -1,6 +1,7 @@ package enumeratum import org.scalatest.{ Matchers, FunSpec } +import org.scalatest.OptionValues._ class EnumSpec extends FunSpec with Matchers { @@ -25,13 +26,45 @@ class EnumSpec extends FunSpec with Matchers { } it("should throw an error otherwise") { - intercept[IllegalArgumentException] { + intercept[NoSuchElementException] { DummyEnum.withName("hello") } } } + describe("#withNameOption") { + + it("should return the proper object when passed the proper string") { + DummyEnum.withNameOption("Hello").value should be(Hello) + DummyEnum.withNameOption("GoodBye").value should be(GoodBye) + DummyEnum.withNameOption("Hi").value should be(Hi) + } + + it("should return None otherwise") { + DummyEnum.withNameOption("hello") shouldBe None + } + + } + + describe("#withNameInsensitiveOption") { + + it("should return the proper object when passed the proper string, disregarding cases") { + DummyEnum.withNameInsensitiveOption("Hello").value should be(Hello) + DummyEnum.withNameInsensitiveOption("hello").value should be(Hello) + DummyEnum.withNameInsensitiveOption("GoodBye").value should be(GoodBye) + DummyEnum.withNameInsensitiveOption("goodBye").value should be(GoodBye) + DummyEnum.withNameInsensitiveOption("gOodbye").value should be(GoodBye) + DummyEnum.withNameInsensitiveOption("Hi").value should be(Hi) + DummyEnum.withNameInsensitiveOption("hI").value should be(Hi) + } + + it("should return None otherwise") { + DummyEnum.withNameInsensitiveOption("bbeeeech") shouldBe None + } + + } + } describe("when a sealed trait is wrapped in another object") { @@ -56,7 +89,7 @@ class EnumSpec extends FunSpec with Matchers { } it("should throw an error otherwise") { - intercept[IllegalArgumentException] { + intercept[NoSuchElementException] { SmartEnum.withName("hello") } } @@ -86,7 +119,7 @@ class EnumSpec extends FunSpec with Matchers { } it("should throw an error otherwise") { - intercept[IllegalArgumentException] { + intercept[NoSuchElementException] { withName("hello") } }