Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace TrimmedString regex with Trimmed predicate type #504

Merged
merged 3 commits into from
May 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ object string extends StringInference {
/** Predicate that checks if a `String` is a valid XPath expression. */
final case class XPath()

/** Predicate that checks if a `String` has no leading or trailing whitespace. */
final case class Trimmed()

object EndsWith {
implicit def endsWithValidate[S <: String](
implicit ws: Witness.Aux[S]): Validate.Plain[String, EndsWith[S]] =
Expand Down Expand Up @@ -234,6 +237,11 @@ object string extends StringInference {
XPath()
)
}

object Trimmed {
implicit def trimmedValidate: Validate.Plain[String, Trimmed] =
Validate.fromPredicate(s => s.trim == s, t => s"$t is trimmed", Trimmed())
}
}

private[refined] trait StringInference {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package eu.timepit.refined.types
import eu.timepit.refined.W
import eu.timepit.refined.api.{Refined, RefinedType, RefinedTypeOps}
import eu.timepit.refined.collection.{MaxSize, NonEmpty}
import eu.timepit.refined.string.MatchesRegex
import eu.timepit.refined.string.{MatchesRegex, Trimmed}
import shapeless.Witness

/** Module for `String` refined types. */
Expand Down Expand Up @@ -52,9 +52,13 @@ object string {
object NonEmptyString extends RefinedTypeOps[NonEmptyString, String]

/** A `String` that contains no leading or trailing whitespace. */
type TrimmedString = String Refined MatchesRegex[W.`"""^(?!\\s).*(?<!\\s)"""`.T]
type TrimmedString = String Refined Trimmed

object TrimmedString extends RefinedTypeOps[TrimmedString, String]
object TrimmedString extends RefinedTypeOps[TrimmedString, String] {

/** Creates a `TrimmedString` from `s` by trimming it. */
def trim(s: String): TrimmedString = Refined.unsafeApply(s.trim)
}

/** A `String` representing a hexadecimal number */
type HexStringSpec = MatchesRegex[W.`"""^(([0-9a-f]+)|([0-9A-F]+))$"""`.T]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ class StringTypesSpec extends Properties("StringTypes") {
(truncated.value ?= str.take(FString3.maxLength))
}

property("""TrimmedString.trim(str)""") = forAll { (str: String) =>
val trimmed = TrimmedString.trim(str)
TrimmedString.from(str) ?= Right(trimmed)
}

// Hashes for ""
object EmptyString {
val md5 = "d41d8cd98f00b204e9800998ecf8427e"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ object all
with NumericInstances
with RefTypeInstances
with StringInstances
with StringInstancesBinCompat1
with CollectionInstances
with CollectionInstancesBinCompat1
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ package eu.timepit.refined.scalacheck

import eu.timepit.refined.api.{Refined, RefType}
import eu.timepit.refined.collection.{NonEmpty, Size}
import eu.timepit.refined.string.{EndsWith, StartsWith}
import eu.timepit.refined.string.{EndsWith, StartsWith, Trimmed}
import eu.timepit.refined.types.string.TrimmedString
import org.scalacheck.Arbitrary
import shapeless.Witness

/**
* Module that provides `Arbitrary` instances for `String` related
* predicates.
*/
object string extends StringInstances
object string extends StringInstances with StringInstancesBinCompat1

trait StringInstances {

Expand Down Expand Up @@ -38,3 +39,10 @@ trait StringInstances {
): Arbitrary[F[String, Size[P]]] =
collection.buildableSizeArbitrary[F, String, Char, P]
}

trait StringInstancesBinCompat1 {
implicit def trimmedStringArbitrary[F[_, _]](
implicit rt: RefType[F]
): Arbitrary[F[String, Trimmed]] =
arbitraryRefType(Arbitrary.arbString.arbitrary.map(TrimmedString.trim(_).value))
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import eu.timepit.refined.scalacheck.generic._
import eu.timepit.refined.scalacheck.numeric._
import eu.timepit.refined.scalacheck.string._
import eu.timepit.refined.string._
import eu.timepit.refined.types.string.{FiniteString, NonEmptyString}
import eu.timepit.refined.types.string.{FiniteString, NonEmptyString, TrimmedString}
import org.scalacheck.Properties

class StringArbitrarySpec extends Properties("StringArbitrary") {
Expand All @@ -19,6 +19,8 @@ class StringArbitrarySpec extends Properties("StringArbitrary") {

property("NonEmptyString") = checkArbitraryRefinedType[NonEmptyString]

property("TrimmedString") = checkArbitraryRefinedType[TrimmedString]

property("MaxSize[16]") = checkArbitraryRefinedType[String Refined MaxSize[W.`16`.T]]

property("FiniteString[10]") = checkArbitraryRefinedType[FiniteString[W.`10`.T]]
Expand Down
2 changes: 1 addition & 1 deletion scalastyle-config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
</check>
<check level="error" class="org.scalastyle.scalariform.NumberOfTypesChecker" enabled="true">
<parameters>
<parameter name="maxTypes"><![CDATA[40]]></parameter>
<parameter name="maxTypes"><![CDATA[45]]></parameter>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding Trimmed bumped us over the limit here. 41 seemed a bit too arbitrary so I bumped this to 45. I will not attempt to defend this solution.

</parameters>
</check>
<check level="error" class="org.scalastyle.scalariform.CyclomaticComplexityChecker" enabled="true">
Expand Down