Skip to content

Commit

Permalink
Merge c8a50f4 into ed45b0e
Browse files Browse the repository at this point in the history
  • Loading branch information
pdalpra committed Dec 28, 2019
2 parents ed45b0e + c8a50f4 commit 77aebbd
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 4 deletions.
28 changes: 28 additions & 0 deletions enumeratum-core/src/main/scala/enumeratum/Enum.scala
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ trait Enum[A <: EnumEntry] {
*/
def withNameOption(name: String): Option[A] = namesToValuesMap.get(name)

/**
* Returns an [[Right[A]]] for a given name, or a [[Left[String]]] if the name does not match any of the values'
* .entryName values.
*/
def withNameEither(name: String): Either[NoSuchMember[EnumEntry], A] =
namesToValuesMap.get(name).toRight(NoSuchMember(name, values))

/**
* Tries to get an [[A]] by the supplied name. The name corresponds to the .name
* of the case objects implementing [[A]], disregarding case
Expand Down Expand Up @@ -139,6 +146,27 @@ trait Enum[A <: EnumEntry] {
def withNameLowercaseOnlyOption(name: String): Option[A] =
lowerCaseNamesToValuesMap.get(name)

/**
* Returns an [[Right[A]]] for a given name, or a [[Left[String]]] if the name does not match any of the values'
* .entryName values, disregarding case.
*/
def withNameInsensitiveEither(name: String): Either[String, A] =
lowerCaseNamesToValuesMap.get(name.toLowerCase).toRight(buildNotFoundMessage(name))

/**
* Returns an [[Right[A]]] for a given name, or a [[Left[String]]] if the name does not match any of the values'
* .entryName values, disregarding case.
*/
def withNameUppercaseOnlyEither(name: String): Either[String, A] =
upperCaseNameValuesToMap.get(name).toRight(buildNotFoundMessage(name))

/**
* Returns an [[Right[A]]] for a given name, or a [[Left[String]]] if the name does not match any of the values'
* .entryName values, disregarding case.
*/
def withNameLowercaseOnlyEither(name: String): Either[String, A] =
lowerCaseNamesToValuesMap.get(name).toRight(buildNotFoundMessage(name))

/**
* Returns the index number of the member passed in the values picked up by this enum
*
Expand Down
10 changes: 10 additions & 0 deletions enumeratum-core/src/main/scala/enumeratum/NoSuchMember.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package enumeratum

import scala.util.control.NoStackTrace

final case class NoSuchMember[A <: EnumEntry](notFoundName: String, enumValues: IndexedSeq[A])
extends NoSuchElementException
with NoStackTrace {
override def getMessage: String =
s"$notFoundName is not a member of Enum (${enumValues.map(_.entryName).mkString(", ")})"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package enumeratum.values

import scala.util.control.NoStackTrace

final case class NoSuchMember[ValueType, A <: ValueEnumEntry[ValueType]](notFoundValue: ValueType,
enumValues: IndexedSeq[A])
extends NoSuchElementException
with NoStackTrace {
override def getMessage: String =
s"$notFoundValue is not a member of ValueEnum (${enumValues.map(_.value).mkString(", ")})"
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ sealed trait ValueEnum[ValueType, EntryType <: ValueEnumEntry[ValueType]] {
*/
def withValueOpt(i: ValueType): Option[EntryType] = valuesToEntriesMap.get(i)

/**
* Returns an [[Right[EntryType]]] for a given value, or a [[Left[String]]] if the value does not match any of the values'
* `.value` values.
*/
def withValueEither(
i: ValueType): Either[NoSuchMember[ValueType, ValueEnumEntry[ValueType]], EntryType] =
valuesToEntriesMap.get(i).toRight(NoSuchMember(i, values))

private lazy val existingEntriesString = values.map(_.value).mkString(", ")

private def buildNotFoundMessage(i: ValueType): String = {
Expand Down
85 changes: 81 additions & 4 deletions enumeratum-core/src/test/scala/enumeratum/EnumSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@ class EnumSpec extends FunSpec with Matchers {

}

describe("#withNameEither") {

it("should return the proper object when passed the proper string") {
DummyEnum.withNameEither("Hello") should be(Right(Hello))
DummyEnum.withNameEither("GoodBye") should be(Right(GoodBye))
DummyEnum.withNameEither("Hi") should be(Right(Hi))
}

it("should return Left otherwise") {
DummyEnum.withNameEither("hello") shouldBe Left(NoSuchMember("hello", DummyEnum.values))
}

}

describe("#withNameInsensitive") {

it("should return the proper object when passed the proper string, disregarding cases") {
Expand Down Expand Up @@ -93,14 +107,33 @@ class EnumSpec extends FunSpec with Matchers {

}

describe("#withNameInsensitiveEither") {

it("should return the proper object when passed the proper string, disregarding cases") {
DummyEnum.withNameInsensitiveEither("Hello") should be(Right(Hello))
DummyEnum.withNameInsensitiveEither("hello") should be(Right(Hello))
DummyEnum.withNameInsensitiveEither("GoodBye") should be(Right(GoodBye))
DummyEnum.withNameInsensitiveEither("goodBye") should be(Right(GoodBye))
DummyEnum.withNameInsensitiveEither("gOodbye") should be(Right(GoodBye))
DummyEnum.withNameInsensitiveEither("Hi") should be(Right(Hi))
DummyEnum.withNameInsensitiveEither("hI") should be(Right(Hi))
}

it("should return Left otherwise") {
DummyEnum.withNameInsensitiveEither("bbeeeech") shouldBe Left(
"bbeeeech is not a member of Enum (Hello, GoodBye, Hi)")
}

}

describe("#withNameUppercaseOnly") {
it("should return the proper object when passed the proper string, transforming to upper case first") {
DummyEnum.withNameUppercaseOnly("HELLO") should be(Hello)
DummyEnum.withNameUppercaseOnly("GOODBYE") should be(GoodBye)
DummyEnum.withNameUppercaseOnly("HI") should be(Hi)
}

it("should return None for not uppercase but case insensitive values") {
it("should throw an error for not uppercase but case insensitive values") {
intercept[NoSuchElementException] {
DummyEnum.withNameUppercaseOnly("Hello")
}
Expand Down Expand Up @@ -137,14 +170,36 @@ class EnumSpec extends FunSpec with Matchers {
}
}

describe("#withNameUppercaseOnlyEither") {
it("should return the proper object when passed the proper string, transforming to upper case first") {
DummyEnum.withNameUppercaseOnlyEither("HELLO") should be(Right(Hello))
DummyEnum.withNameUppercaseOnlyEither("GOODBYE") should be(Right(GoodBye))
DummyEnum.withNameUppercaseOnlyEither("HI") should be(Right(Hi))
}

it("should return Left for not uppercase but case insensitive values") {
DummyEnum.withNameUppercaseOnlyEither("Hello") should be(
Left("Hello is not a member of Enum (Hello, GoodBye, Hi)"))
DummyEnum.withNameUppercaseOnlyEither("GoodBye") should be(
Left("GoodBye is not a member of Enum (Hello, GoodBye, Hi)"))
DummyEnum.withNameUppercaseOnlyEither("Hi") should be(
Left("Hi is not a member of Enum (Hello, GoodBye, Hi)"))
}

it("should return Left otherwise") {
DummyEnum.withNameUppercaseOnlyEither("bbeeeech") should be(
Left("bbeeeech is not a member of Enum (Hello, GoodBye, Hi)"))
}
}

describe("#withNameLowercaseOnly") {
it("should return the proper object when passed the proper string, transforming to lower case first") {
DummyEnum.withNameLowercaseOnly("hello") should be(Hello)
DummyEnum.withNameLowercaseOnly("goodbye") should be(GoodBye)
DummyEnum.withNameLowercaseOnly("hi") should be(Hi)
}

it("should return None for not uppercase but case insensitive values") {
it("should throw an error for not lowercase but case insensitive values") {
intercept[NoSuchElementException] {
DummyEnum.withNameLowercaseOnly("Hello")
}
Expand All @@ -170,16 +225,38 @@ class EnumSpec extends FunSpec with Matchers {
DummyEnum.withNameLowercaseOnlyOption("hi").value should be(Hi)
}

it("should return None for not uppercase but case insensitive values") {
it("should return None for not lowercase but case insensitive values") {
DummyEnum.withNameLowercaseOnlyOption("Hello") should be(None)
DummyEnum.withNameLowercaseOnlyOption("GoodBye") should be(None)
DummyEnum.withNameLowercaseOnlyOption("Hi") should be(None)
}

it("should throw an error otherwise") {
it("should return None otherwise") {
DummyEnum.withNameLowercaseOnlyOption("bbeeeech") should be(None)
}
}

describe("#withNameLowercaseOnlyEither") {
it("should return the proper object when passed the proper string, transforming to lower case first") {
DummyEnum.withNameLowercaseOnlyEither("hello") should be(Right(Hello))
DummyEnum.withNameLowercaseOnlyEither("goodbye") should be(Right(GoodBye))
DummyEnum.withNameLowercaseOnlyEither("hi") should be(Right(Hi))
}

it("should return Left for not lowercase but case insensitive values") {
DummyEnum.withNameLowercaseOnlyEither("Hello") should be(
Left("Hello is not a member of Enum (Hello, GoodBye, Hi)"))
DummyEnum.withNameLowercaseOnlyEither("GoodBye") should be(
Left("GoodBye is not a member of Enum (Hello, GoodBye, Hi)"))
DummyEnum.withNameLowercaseOnlyEither("Hi") should be(
Left("Hi is not a member of Enum (Hello, GoodBye, Hi)"))
}

it("should return Left otherwise") {
DummyEnum.withNameLowercaseOnlyEither("bbeeeech") should be(
Left("bbeeeech is not a member of Enum (Hello, GoodBye, Hi)"))
}
}
}

describe("when a sealed trait is wrapped in another object") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@ trait ValueEnumHelpers { this: FunSpec with Matchers =>

}

describe("withValueEither") {

it("should return Right(entry) that match the value") {
enum.values.foreach { entry =>
enum.withValueEither(entry.value) shouldBe Right(entry)
}
}

it("should return Left when given values that do not map to any entries") {
invalidValues.foreach { invalid =>
enum.withValueEither(invalid) shouldBe Left(NoSuchMember(invalid, enum.values))
}
}

}

describe("in") {

it("should return false if given an empty list") {
Expand Down

0 comments on commit 77aebbd

Please sign in to comment.