From a617545a412a671b58f521b1e4d4b4bfc4116599 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Fromentin?= <42907886+Iltotore@users.noreply.github.com> Date: Thu, 29 Aug 2024 16:53:35 +0200 Subject: [PATCH] build: Upgrade to Scala 3.4.3 (#262) --- .gitignore | 3 +- .mill-version | 2 +- build.sc | 8 +-- .../io/github/iltotore/iron/CatsSuite.scala | 9 ++-- docs/_docs/modules/cats.md | 4 +- docs/_docs/modules/circe.md | 4 +- docs/_docs/modules/zio.md | 4 +- docs/_docs/reference/refinement.md | 4 +- .../iron/borerSerialization/Account.scala | 11 +++-- .../iltotore/iron/formCats/Account.scala | 10 ++-- .../iltotore/iron/formJsoniter/Account.scala | 10 ++-- .../iltotore/iron/formZio/Account.scala | 10 ++-- .../github/iltotore/iron/RefinedTypeOps.scala | 2 +- .../github/iltotore/iron/constraint/any.scala | 6 +-- .../iltotore/iron/constraint/char.scala | 6 +-- .../iltotore/iron/constraint/collection.scala | 12 ++--- .../iltotore/iron/constraint/numeric.scala | 38 ++++++++++---- .../iltotore/iron/constraint/string.scala | 38 +++++++------- .../iltotore/iron/internal/IronConfig.scala | 4 +- .../iltotore/iron/internal/package.scala | 1 - .../iltotore/iron/macros/ReflectUtil.scala | 49 +++++++++---------- .../github/iltotore/iron/macros/package.scala | 4 +- .../iltotore/iron/testing/AnySuite.scala | 4 +- sandbox/src/.gitkeep | 0 zio/src/io/github/iltotore/iron/zio.scala | 12 +++-- .../io/github/iltotore/iron/ZIOSuite.scala | 39 +++++++++------ 26 files changed, 170 insertions(+), 124 deletions(-) create mode 100644 sandbox/src/.gitkeep diff --git a/.gitignore b/.gitignore index d32ba5f0..db5bd67c 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ *.iml tmp out -.history \ No newline at end of file +.history +sandbox \ No newline at end of file diff --git a/.mill-version b/.mill-version index 12edb292..5746d36d 100644 --- a/.mill-version +++ b/.mill-version @@ -1 +1 @@ -0.11.7 \ No newline at end of file +0.11.12 \ No newline at end of file diff --git a/build.sc b/build.sc index bf33fbfc..b03b51b6 100644 --- a/build.sc +++ b/build.sc @@ -7,9 +7,9 @@ import mill._, define._, api.Result import scalalib._, scalalib.scalafmt._, scalalib.publish._, scalajslib._, scalanativelib._ object versions { - val scala = "3.3.1" - val scalaJS = "1.13.2" - val scalaNative = "0.4.15" + val scala = "3.4.3" + val scalaJS = "1.16.0" + val scalaNative = "0.4.17" } trait BaseModule extends ScalaModule with ScalafmtModule with CiReleaseModule { outer => @@ -291,7 +291,7 @@ object cats extends SubModule { object test extends Tests { def ivyDeps = Agg( ivy"com.lihaoyi::utest:0.8.1", - ivy"org.typelevel::kittens:3.0.0" + ivy"org.typelevel::kittens:3.4.0" ) } diff --git a/cats/test/src/io/github/iltotore/iron/CatsSuite.scala b/cats/test/src/io/github/iltotore/iron/CatsSuite.scala index fe5c1035..ebb59c75 100644 --- a/cats/test/src/io/github/iltotore/iron/CatsSuite.scala +++ b/cats/test/src/io/github/iltotore/iron/CatsSuite.scala @@ -166,9 +166,9 @@ object CatsSuite extends TestSuite: test("validatedNel"): test - assert(valid.refineAllValidatedNel[Positive] == Valid(valid)) test - assert(invalid.refineAllValidatedNel[Positive] == Invalid(NonEmptyList.of( - InvalidValue(-2, "Should be strictly positive"), - InvalidValue(-3, "Should be strictly positive") - ))) + InvalidValue(-2, "Should be strictly positive"), + InvalidValue(-3, "Should be strictly positive") + ))) test("further"): @@ -231,6 +231,5 @@ object CatsSuite extends TestSuite: InvalidValue(-2, "Should be strictly positive"), InvalidValue(-3, "Should be strictly positive") ))) - - + } diff --git a/docs/_docs/modules/cats.md b/docs/_docs/modules/cats.md index 126bff1c..dc403bd2 100644 --- a/docs/_docs/modules/cats.md +++ b/docs/_docs/modules/cats.md @@ -74,9 +74,9 @@ import io.github.iltotore.iron.* import io.github.iltotore.iron.cats.* import io.github.iltotore.iron.constraint.all.* //} -type Username = Alphanumeric DescribedAs "Username should be alphanumeric" +type Username = DescribedAs[Alphanumeric, "Username should be alphanumeric"] -type Age = Positive DescribedAs "Age should be positive" +type Age = DescribedAs[Positive, "Age should be positive"] case class User(name: String :| Username, age: Int :| Age) diff --git a/docs/_docs/modules/circe.md b/docs/_docs/modules/circe.md index 5e68049a..31e6a541 100644 --- a/docs/_docs/modules/circe.md +++ b/docs/_docs/modules/circe.md @@ -52,9 +52,9 @@ import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.all.* import io.github.iltotore.iron.circe.given -type Username = Alphanumeric DescribedAs "Username should be alphanumeric" +type Username = DescribedAs[Alphanumeric, "Username should be alphanumeric"] -type Age = Positive DescribedAs "Age should be positive" +type Age = DescribedAs[Positive, "Age should be positive"] case class User(name: String :| Username, age: Int :| Age) diff --git a/docs/_docs/modules/zio.md b/docs/_docs/modules/zio.md index 1096610a..71f56867 100644 --- a/docs/_docs/modules/zio.md +++ b/docs/_docs/modules/zio.md @@ -55,9 +55,9 @@ import io.github.iltotore.iron.constraint.all.* import io.github.iltotore.iron.zio.* -type Username = Alphanumeric DescribedAs "Username should be alphanumeric" +type Username = DescribedAs[Alphanumeric, "Username should be alphanumeric"] -type Age = Positive DescribedAs "Age should be positive" +type Age = DescribedAs[Positive, "Age should be positive"] case class User(name: String :| Username, age: Int :| Age) diff --git a/docs/_docs/reference/refinement.md b/docs/_docs/reference/refinement.md index 286ac8c1..51fc3e15 100644 --- a/docs/_docs/reference/refinement.md +++ b/docs/_docs/reference/refinement.md @@ -137,9 +137,9 @@ import io.github.iltotore.iron.constraint.all.* import io.github.iltotore.iron.zio.* -type Username = Alphanumeric DescribedAs "Username should be alphanumeric" +type Username = DescribedAs[Alphanumeric, "Username should be alphanumeric"] -type Age = Positive DescribedAs "Age should be positive" +type Age = DescribedAs[Positive, "Age should be positive"] case class User(name: String :| Username, age: Int :| Age) diff --git a/examples/borerSerialization/src/io/github/iltotore/iron/borerSerialization/Account.scala b/examples/borerSerialization/src/io/github/iltotore/iron/borerSerialization/Account.scala index 4164567e..fde534c5 100644 --- a/examples/borerSerialization/src/io/github/iltotore/iron/borerSerialization/Account.scala +++ b/examples/borerSerialization/src/io/github/iltotore/iron/borerSerialization/Account.scala @@ -6,14 +6,17 @@ import io.github.iltotore.iron.constraint.all.* import io.github.iltotore.iron.* import io.github.iltotore.iron.borer.given // this enables borer <-> iron integration -type Username = (Alphanumeric & MinLength[3] & MaxLength[10]) DescribedAs +type Username = DescribedAs[ + Alphanumeric & MinLength[3] & MaxLength[10], "Username should be alphanumeric and have a length between 3 and 10" +] -type Password = (Match["[A-Za-z].*[0-9]|[0-9].*[A-Za-z]"] & MinLength[6] & MaxLength[20]) DescribedAs +type Password = DescribedAs[ + Match["[A-Za-z].*[0-9]|[0-9].*[A-Za-z]"] & MinLength[6] & MaxLength[20], "Password must contain at least a letter, a digit and have a length between 6 and 20" +] -type Age = Greater[0] DescribedAs - "Age should be strictly positive" +type Age = DescribedAs[Greater[0], "Age should be strictly positive"] case class Account( name: String :| Username, diff --git a/examples/formCats/src/io/github/iltotore/iron/formCats/Account.scala b/examples/formCats/src/io/github/iltotore/iron/formCats/Account.scala index 8d179961..078da240 100644 --- a/examples/formCats/src/io/github/iltotore/iron/formCats/Account.scala +++ b/examples/formCats/src/io/github/iltotore/iron/formCats/Account.scala @@ -13,13 +13,17 @@ import io.github.iltotore.iron.constraint.all.* import org.http4s.{EntityDecoder, EntityEncoder} import org.http4s.circe.* -type Username = (Alphanumeric & MinLength[3] & MaxLength[10]) DescribedAs +type Username = DescribedAs[ + Alphanumeric & MinLength[3] & MaxLength[10], "Username should be alphanumeric and have a length between 3 and 10" +] -type Password = (Match["[A-Za-z].*[0-9]|[0-9].*[A-Za-z]"] & MinLength[6] & MaxLength[20]) DescribedAs +type Password = DescribedAs[ + Match["[A-Za-z].*[0-9]|[0-9].*[A-Za-z]"] & MinLength[6] & MaxLength[20], "Password must contain atleast a letter, a digit and have a length between 6 and 20" +] -type Age = Greater[0] DescribedAs "Age should be strictly positive" +type Age = DescribedAs[Greater[0], "Age should be strictly positive"] /** * A basic Account with a name, a password and an age. diff --git a/examples/formJsoniter/src/io/github/iltotore/iron/formJsoniter/Account.scala b/examples/formJsoniter/src/io/github/iltotore/iron/formJsoniter/Account.scala index 92e70e7c..1640898c 100644 --- a/examples/formJsoniter/src/io/github/iltotore/iron/formJsoniter/Account.scala +++ b/examples/formJsoniter/src/io/github/iltotore/iron/formJsoniter/Account.scala @@ -8,13 +8,17 @@ import io.github.iltotore.iron.{*, given} import scala.util.Try -type Username = (Alphanumeric & MinLength[3] & MaxLength[10]) DescribedAs +type Username = DescribedAs[ + Alphanumeric & MinLength[3] & MaxLength[10], "Username should be alphanumeric and have a length between 3 and 10" +] -type Password = (Match["[A-Za-z].*[0-9]|[0-9].*[A-Za-z]"] & MinLength[6] & MaxLength[20]) DescribedAs +type Password = DescribedAs[ + Match["[A-Za-z].*[0-9]|[0-9].*[A-Za-z]"] & MinLength[6] & MaxLength[20], "Password must contain atleast a letter, a digit and have a length between 6 and 20" +] -type Age = Greater[0] DescribedAs "Age should be strictly positive" +type Age = DescribedAs[Greater[0], "Age should be strictly positive"] /** * A basic Account with a name, a password and an age. diff --git a/examples/formZio/src/io/github/iltotore/iron/formZio/Account.scala b/examples/formZio/src/io/github/iltotore/iron/formZio/Account.scala index af34e2a5..af74d7f7 100644 --- a/examples/formZio/src/io/github/iltotore/iron/formZio/Account.scala +++ b/examples/formZio/src/io/github/iltotore/iron/formZio/Account.scala @@ -5,13 +5,17 @@ import io.github.iltotore.iron.zioJson.given import zio.json.* -type Username = (Alphanumeric & MinLength[3] & MaxLength[10]) DescribedAs +type Username = DescribedAs[ + Alphanumeric & MinLength[3] & MaxLength[10], "Username should be alphanumeric and have a length between 3 and 10" +] -type Password = (Match["[A-Za-z].*[0-9]|[0-9].*[A-Za-z]"] & MinLength[6] & MaxLength[20]) DescribedAs +type Password = DescribedAs[ + Match["[A-Za-z].*[0-9]|[0-9].*[A-Za-z]"] & MinLength[6] & MaxLength[20], "Password must contain atleast a letter, a digit and have a length between 6 and 20" +] -type Age = Greater[0] DescribedAs "Age should be strictly positive" +type Age = DescribedAs[Greater[0], "Age should be strictly positive"] /** * A basic Account with a name, a password and an age. diff --git a/main/src/io/github/iltotore/iron/RefinedTypeOps.scala b/main/src/io/github/iltotore/iron/RefinedTypeOps.scala index 4004981d..1059fe2e 100644 --- a/main/src/io/github/iltotore/iron/RefinedTypeOps.scala +++ b/main/src/io/github/iltotore/iron/RefinedTypeOps.scala @@ -177,4 +177,4 @@ object RefinedTypeOps: * //FinalType =/= IronType * }}} */ - type FinalType = T \ No newline at end of file + type FinalType = T diff --git a/main/src/io/github/iltotore/iron/constraint/any.scala b/main/src/io/github/iltotore/iron/constraint/any.scala index 4ca7a92f..5d584fb9 100644 --- a/main/src/io/github/iltotore/iron/constraint/any.scala +++ b/main/src/io/github/iltotore/iron/constraint/any.scala @@ -36,7 +36,7 @@ object any: * @tparam V the description to attach. * @example {{{ * //Literal - * type PosInt = Greater[0] DescribedAs "Should be positive" + * type PosInt = DescribedAs[Greater[0], "Should be positive"] * * //Using type-level String concatenation (example taken from `numeric`) * import io.github.iltotore.iron.ops.* @@ -123,12 +123,12 @@ object any: /** * A described constraint C1 implies C1. */ - given [C1, C2, V <: String](using C1 ==> C2): ((C1 DescribedAs V) ==> C2) = Implication() + given [C1, C2, V <: String](using C1 ==> C2): (DescribedAs[C1, V] ==> C2) = Implication() /** * A constraint C1 implies its "described" form. */ - given [C1, C2, V <: String](using C1 ==> C2): (C1 ==> (C2 DescribedAs V)) = Implication() + given [C1, C2, V <: String](using C1 ==> C2): (C1 ==> DescribedAs[C2, V]) = Implication() object Not: class NotConstraint[A, C, Impl <: Constraint[A, C]](using Impl) extends Constraint[A, Not[C]]: diff --git a/main/src/io/github/iltotore/iron/constraint/char.scala b/main/src/io/github/iltotore/iron/constraint/char.scala index e88a47ec..d66ec66c 100644 --- a/main/src/io/github/iltotore/iron/constraint/char.scala +++ b/main/src/io/github/iltotore/iron/constraint/char.scala @@ -82,7 +82,7 @@ object char: private def check(expr: Expr[Char])(using Quotes): Expr[Boolean] = val rflUtil = reflectUtil import rflUtil.* - + expr.decode match case Right(value) => Expr(value.isUpper) case _ => '{ $expr.isUpper } @@ -98,7 +98,7 @@ object char: private def check(expr: Expr[Char])(using Quotes): Expr[Boolean] = val rflUtil = reflectUtil import rflUtil.* - + expr.decode match case Right(value) => Expr(value.isDigit) case _ => '{ $expr.isDigit } @@ -114,7 +114,7 @@ object char: private def check(expr: Expr[Char])(using Quotes): Expr[Boolean] = val rflUtil = reflectUtil import rflUtil.* - + expr.decode match case Right(value) => Expr(value.isLetter) case _ => '{ $expr.isLetter } diff --git a/main/src/io/github/iltotore/iron/constraint/collection.scala b/main/src/io/github/iltotore/iron/constraint/collection.scala index 22b8cd4f..034ec2c4 100644 --- a/main/src/io/github/iltotore/iron/constraint/collection.scala +++ b/main/src/io/github/iltotore/iron/constraint/collection.scala @@ -28,24 +28,24 @@ object collection: * * @tparam V the minimum length of the tested input */ - type MinLength[V <: Int] = Length[GreaterEqual[V]] DescribedAs "Should have a minimum length of " + V + type MinLength[V <: Int] = DescribedAs[Length[GreaterEqual[V]], "Should have a minimum length of " + V] /** * Tests maximum length. Supports [[Iterable]] and [[String]] by default. * * @tparam V the maximum length of the tested input */ - type MaxLength[V <: Int] = Length[LessEqual[V]] DescribedAs "Should have a maximum length of " + V + type MaxLength[V <: Int] = DescribedAs[Length[LessEqual[V]], "Should have a maximum length of " + V] /** * Tests exact length. Supports [[Iterable]] and [[String]] by default. */ - type FixedLength[V <: Int] = Length[StrictEqual[V]] DescribedAs "Should have an exact length of " + V + type FixedLength[V <: Int] = DescribedAs[Length[StrictEqual[V]], "Should have an exact length of " + V] /** * Tests if the input is empty. */ - type Empty = FixedLength[0] DescribedAs "Should be empty" + type Empty = DescribedAs[FixedLength[0], "Should be empty"] /** * Tests if the given collection contains a specific value. @@ -117,7 +117,7 @@ object collection: expr.decode match case Right(value) => applyConstraint(Expr(value.length), constraintExpr) - case _ => applyConstraint('{ $expr.length }, constraintExpr) + case _ => applyConstraint('{ $expr.length }, constraintExpr) given [C1, C2](using C1 ==> C2): (Length[C1] ==> Length[C2]) = Implication() @@ -140,7 +140,7 @@ object collection: (expr.decode, partExpr.decode) match case (Right(value), Right(part)) => Expr(value.contains(part)) - case _ => '{ ${ expr }.contains($partExpr) } + case _ => '{ ${ expr }.contains($partExpr) } object ForAll: diff --git a/main/src/io/github/iltotore/iron/constraint/numeric.scala b/main/src/io/github/iltotore/iron/constraint/numeric.scala index ec1861be..f85db200 100644 --- a/main/src/io/github/iltotore/iron/constraint/numeric.scala +++ b/main/src/io/github/iltotore/iron/constraint/numeric.scala @@ -30,34 +30,40 @@ object numeric: * * @tparam V the value the input must be greater than or equal to. */ - type GreaterEqual[V] = (Greater[V] | StrictEqual[V]) DescribedAs ("Should be greater than or equal to " + V) + type GreaterEqual[V] = DescribedAs[ + Greater[V] | StrictEqual[V], + "Should be greater than or equal to " + V + ] /** * Tests non-strict inferiority. * * @tparam V the value the input must be less than or equal to. */ - type LessEqual[V] = (Less[V] | StrictEqual[V]) DescribedAs ("Should be less than or equal to " + V) + type LessEqual[V] = DescribedAs[ + Less[V] | StrictEqual[V], + "Should be less than or equal to " + V + ] /** * Tests if the input is strictly positive. */ - type Positive = Greater[0] DescribedAs "Should be strictly positive" + type Positive = DescribedAs[Greater[0], "Should be strictly positive"] /** * Tests if the input is positive or zero. */ - type Positive0 = GreaterEqual[0] DescribedAs "Should be positive or zero" + type Positive0 = DescribedAs[GreaterEqual[0], "Should be positive or zero"] /** * Tests if the input is strictly negative. */ - type Negative = Less[0] DescribedAs "Should be strictly negative" + type Negative = DescribedAs[Less[0], "Should be strictly negative"] /** * Tests if the input is negative or zero. */ - type Negative0 = LessEqual[0] DescribedAs "Should be negative or zero" + type Negative0 = DescribedAs[LessEqual[0], "Should be negative or zero"] object Interval: @@ -67,7 +73,10 @@ object numeric: * @tparam V1 the lower bound, exclusive. * @tparam V2 the upper bound, exclusive. */ - type Open[V1, V2] = (Greater[V1] & Less[V2]) DescribedAs ("Should be included in (" + V1 + ", " + V2 + ")") + type Open[V1, V2] = DescribedAs[ + Greater[V1] & Less[V2], + "Should be included in (" + V1 + ", " + V2 + ")" + ] /** * Tests if the input is included in `(V1, V2]` @@ -75,7 +84,10 @@ object numeric: * @tparam V1 the lower bound, exclusive. * @tparam V2 the upper bound, inclusive. */ - type OpenClosed[V1, V2] = (Greater[V1] & LessEqual[V2]) DescribedAs ("Should be included in (" + V1 + ", " + V2 + "]") + type OpenClosed[V1, V2] = DescribedAs[ + Greater[V1] & LessEqual[V2], + "Should be included in (" + V1 + ", " + V2 + "]" + ] /** * Tests if the input is included in `[V1, V2)` @@ -83,7 +95,10 @@ object numeric: * @tparam V1 the lower bound, inclusive. * @tparam V2 the upper bound, exclusive. */ - type ClosedOpen[V1, V2] = (GreaterEqual[V1] & Less[V2]) DescribedAs ("Should be included in [" + V1 + ", " + V2 + ")") + type ClosedOpen[V1, V2] = DescribedAs[ + GreaterEqual[V1] & Less[V2], + "Should be included in [" + V1 + ", " + V2 + ")" + ] /** * Tests if the input is included in `[V1, V2]` @@ -91,7 +106,10 @@ object numeric: * @tparam V1 the lower bound, inclusive. * @tparam V2 the upper bound, inclusive. */ - type Closed[V1, V2] = (GreaterEqual[V1] & LessEqual[V2]) DescribedAs ("Should be included in [" + V1 + ", " + V2 + "]") + type Closed[V1, V2] = DescribedAs[ + GreaterEqual[V1] & LessEqual[V2], + "Should be included in [" + V1 + ", " + V2 + "]" + ] /** * Tests if the input is a multiple of V. diff --git a/main/src/io/github/iltotore/iron/constraint/string.scala b/main/src/io/github/iltotore/iron/constraint/string.scala index a8d36433..7a4ed2c1 100644 --- a/main/src/io/github/iltotore/iron/constraint/string.scala +++ b/main/src/io/github/iltotore/iron/constraint/string.scala @@ -21,29 +21,31 @@ object string: * Tests if the input only contains whitespaces. * @see [[Whitespace]] */ - type Blank = ForAll[Whitespace] DescribedAs "Should only contain whitespaces" + type Blank = DescribedAs[ForAll[Whitespace], "Should only contain whitespaces"] /** * Tests if the input does not have leading or trailing whitespaces. * @see [[Whitespace]] */ - type Trimmed = (Empty | Not[Head[Whitespace] | Last[Whitespace]]) DescribedAs + type Trimmed = DescribedAs[ + Empty | Not[Head[Whitespace] | Last[Whitespace]], "Should not have leading or trailing whitespaces" + ] /** * Tests if all letters of the input are lower cased. */ - type LettersLowerCase = ForAll[Not[Letter] | LowerCase] DescribedAs "All letters should be lower cased" + type LettersLowerCase = DescribedAs[ForAll[Not[Letter] | LowerCase], "All letters should be lower cased"] /** * Tests if all letters of the input are upper cased. */ - type LettersUpperCase = ForAll[Not[Letter] | UpperCase] DescribedAs "All letters should be upper cased" + type LettersUpperCase = DescribedAs[ForAll[Not[Letter] | UpperCase], "All letters should be upper cased"] /** * Tests if the input only contains alphanumeric characters. */ - type Alphanumeric = ForAll[Digit | Letter] DescribedAs "Should be alphanumeric" + type Alphanumeric = DescribedAs[ForAll[Digit | Letter], "Should be alphanumeric"] /** * Tests if the input starts with the given prefix. @@ -70,16 +72,18 @@ object string: * * @note it only checks if the input fits the URL pattern. Not if the given URL exists/is accessible. */ - type ValidURL = - Match[ - "((\\w+:)+\\/\\/)?(([-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,63})|(localhost))(:\\d{1,5})?(\\/|\\/([-a-zA-Z0-9@:%_\\+.~#?&//=]*))?" - ] DescribedAs "Should be an URL" + type ValidURL = DescribedAs[ + Match["((\\w+:)+\\/\\/)?(([-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,63})|(localhost))(:\\d{1,5})?(\\/|\\/([-a-zA-Z0-9@:%_\\+.~#?&//=]*))?"], + "Should be an URL" + ] /** * Tests if the input is a valid UUID. */ - type ValidUUID = - Match["^([0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12})"] DescribedAs "Should be an UUID" + type ValidUUID = DescribedAs[ + Match["^([0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12})"], + "Should be an UUID" + ] /** * Tests if the input is a valid semantic version as defined in [semver site](https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string). @@ -103,10 +107,10 @@ object string: private def check(expr: Expr[String], prefixExpr: Expr[String])(using Quotes): Expr[Boolean] = val rflUtil = reflectUtil import rflUtil.* - + (expr.decode, prefixExpr.decode) match case (Right(value), Right(prefix)) => Expr(value.startsWith(prefix)) - case _ => '{ $expr.startsWith($prefixExpr) } + case _ => '{ $expr.startsWith($prefixExpr) } object EndWith: @@ -119,10 +123,10 @@ object string: private def check(expr: Expr[String], prefixExpr: Expr[String])(using Quotes): Expr[Boolean] = val rflUtil = reflectUtil import rflUtil.* - + (expr.decode, prefixExpr.decode) match case (Right(value), Right(prefix)) => Expr(value.endsWith(prefix)) - case _ => '{ $expr.endsWith($prefixExpr) } + case _ => '{ $expr.endsWith($prefixExpr) } object Match: @@ -135,7 +139,7 @@ object string: private def check(valueExpr: Expr[String], regexExpr: Expr[String])(using Quotes): Expr[Boolean] = val rflUtil = reflectUtil import rflUtil.* - + (valueExpr.decode, regexExpr.decode) match case (Right(value), Right(regex)) => Expr(value.matches(regex)) - case _ => '{ $valueExpr.matches($regexExpr) } + case _ => '{ $valueExpr.matches($regexExpr) } diff --git a/main/src/io/github/iltotore/iron/internal/IronConfig.scala b/main/src/io/github/iltotore/iron/internal/IronConfig.scala index e042cf92..3bbc9c7a 100644 --- a/main/src/io/github/iltotore/iron/internal/IronConfig.scala +++ b/main/src/io/github/iltotore/iron/internal/IronConfig.scala @@ -2,7 +2,7 @@ package io.github.iltotore.iron.internal /** * The config or Iron at compile-time. - * + * * @param color enable colored messages * @param shortMessages use abbreviated messages, useful for error lenses and similar */ @@ -15,5 +15,5 @@ object IronConfig: */ val fromSystem: IronConfig = IronConfig( color = sys.props.get("iron.color").orElse(sys.env.get("IRON_COLOR")).flatMap(_.toBooleanOption).getOrElse(true), - shortMessages = sys.props.get("iron.shortMessages").orElse(sys.env.get("IRON_SHORT_MESSAGES")).flatMap(_.toBooleanOption).getOrElse(false), + shortMessages = sys.props.get("iron.shortMessages").orElse(sys.env.get("IRON_SHORT_MESSAGES")).flatMap(_.toBooleanOption).getOrElse(false) ) diff --git a/main/src/io/github/iltotore/iron/internal/package.scala b/main/src/io/github/iltotore/iron/internal/package.scala index 905bf32a..bc49409e 100644 --- a/main/src/io/github/iltotore/iron/internal/package.scala +++ b/main/src/io/github/iltotore/iron/internal/package.scala @@ -1,7 +1,6 @@ package io.github.iltotore.iron.internal extension (text: String) - def colorized(color: String)(using config: IronConfig): String = if config.color then s"$color$text${Console.RESET}" else text diff --git a/main/src/io/github/iltotore/iron/macros/ReflectUtil.scala b/main/src/io/github/iltotore/iron/macros/ReflectUtil.scala index 231addf2..3e157ac0 100644 --- a/main/src/io/github/iltotore/iron/macros/ReflectUtil.scala +++ b/main/src/io/github/iltotore/iron/macros/ReflectUtil.scala @@ -21,7 +21,6 @@ class ReflectUtil[Q <: Quotes & Singleton](using val _quotes: Q): import _quotes.reflect.* extension [T: Type](expr: Expr[T]) - /** * Decode this expression. * @@ -113,7 +112,7 @@ class ReflectUtil[Q <: Quotes & Singleton](using val _quotes: Q): */ def prettyPrint(bodyIdent: Int = 0, firstLineIdent: Int = 0)(using Printer[Tree]): String = val unindented = this match - case NotInlined(term) => s"Term not inlined: ${term.show}" + case NotInlined(term) => s"Term not inlined: ${term.show}" case DefinitionNotInlined(name) => s"Definition not inlined: $name. Only vals and zero-arg def can be inlined." case HasBindings(defFailures) => val failures = defFailures @@ -167,9 +166,9 @@ class ReflectUtil[Q <: Quotes & Singleton](using val _quotes: Q): case StringPartsNotInlined(parts) => val errors = parts .zipWithIndex - .collect: - case (Left(failure), i) => s"Arg $i:\n${failure.prettyPrint(2, 2)}" - .mkString("\n\n") + .collect: + case (Left(failure), i) => s"Arg $i:\n${failure.prettyPrint(2, 2)}" + .mkString("\n\n") s"String contatenation has non inlined arguments:\n$errors" @@ -183,7 +182,7 @@ class ReflectUtil[Q <: Quotes & Singleton](using val _quotes: Q): private val enhancedDecoders: Map[TypeRepr, (Term, Map[String, ?]) => Either[DecodingFailure, ?]] = Map( TypeRepr.of[Boolean] -> decodeBoolean, - TypeRepr.of[String] -> decodeString + TypeRepr.of[String] -> decodeString ) /** @@ -203,7 +202,7 @@ class ReflectUtil[Q <: Quotes & Singleton](using val _quotes: Q): specializedResult match case Left(DecodingFailure.Unknown) => decodeUnspecializedTerm(tree, definitions) - case result => result.asInstanceOf[Either[DecodingFailure, T]] + case result => result.asInstanceOf[Either[DecodingFailure, T]] /** * Decode a term using only unspecialized cases. @@ -215,24 +214,24 @@ class ReflectUtil[Q <: Quotes & Singleton](using val _quotes: Q): */ def decodeUnspecializedTerm[T](tree: Term, definitions: Map[String, ?]): Either[DecodingFailure, T] = tree match - case block@Block(stats, e) => if stats.isEmpty then decodeTerm(e, definitions) else Left(DecodingFailure.HasStatements(block)) + case block @ Block(stats, e) => if stats.isEmpty then decodeTerm(e, definitions) else Left(DecodingFailure.HasStatements(block)) case Inlined(_, bindings, e) => val (failures, values) = bindings .map[(String, Either[DecodingFailure, ?])](b => (b.name, decodeBinding(b, definitions))) .partitionMap: - case (name, Right(value)) => Right((name, value)) + case (name, Right(value)) => Right((name, value)) case (name, Left(failure)) => Left((name, failure)) (failures, decodeTerm[T](e, definitions ++ values.toMap)) match case (_, Right(value)) => Right(value) case (Nil, Left(failure)) => Left(failure) - case (failures, Left(_)) => Left(DecodingFailure.HasBindings(failures)) + case (failures, Left(_)) => Left(DecodingFailure.HasBindings(failures)) case Apply(Select(left, "=="), List(right)) => (decodeTerm[Any](left, definitions), decodeTerm[Any](right, definitions)) match - case (Right(leftValue), Right(rightValue)) => Right((leftValue == rightValue).asInstanceOf[T]) - case (leftResult, rightResult) => Left(DecodingFailure.ApplyNotInlined("==", List(leftResult, rightResult))) + case (Right(leftValue), Right(rightValue)) => Right((leftValue == rightValue).asInstanceOf[T]) + case (leftResult, rightResult) => Left(DecodingFailure.ApplyNotInlined("==", List(leftResult, rightResult))) case Apply(Select(leftOperand, name), operands) => val rightResults = operands.map(decodeTerm(_, definitions)) @@ -259,14 +258,14 @@ class ReflectUtil[Q <: Quotes & Singleton](using val _quotes: Q): case Typed(e, _) => decodeTerm(e, definitions) case Ident(name) => definitions - .get(name) - .toRight(DecodingFailure.NotInlined(tree)) - .asInstanceOf[Either[DecodingFailure, T]] + .get(name) + .toRight(DecodingFailure.NotInlined(tree)) + .asInstanceOf[Either[DecodingFailure, T]] case _ => tree.tpe.widenTermRefByName match case ConstantType(c) => Right(c.value.asInstanceOf[T]) - case _ => Left(DecodingFailure.NotInlined(tree)) + case _ => Left(DecodingFailure.NotInlined(tree)) /** * Decode a binding/definition. @@ -277,9 +276,9 @@ class ReflectUtil[Q <: Quotes & Singleton](using val _quotes: Q): * @return the value of the given definition found at compile time or a [[DecodingFailure]] */ def decodeBinding[T](definition: Definition, definitions: Map[String, ?]): Either[DecodingFailure, T] = definition match - case ValDef(name, tpeTree, Some(term)) => decodeTerm(term, definitions) + case ValDef(name, tpeTree, Some(term)) => decodeTerm(term, definitions) case DefDef(name, Nil, tpeTree, Some(term)) => decodeTerm(term, definitions) - case _ => Left(DecodingFailure.DefinitionNotInlined(definition.name)) + case _ => Left(DecodingFailure.DefinitionNotInlined(definition.name)) /** * Decode a [[Boolean]] term using only [[Boolean]]-specific cases. @@ -291,17 +290,17 @@ class ReflectUtil[Q <: Quotes & Singleton](using val _quotes: Q): def decodeBoolean(term: Term, definitions: Map[String, ?]): Either[DecodingFailure, Boolean] = term match case Apply(Select(left, "||"), List(right)) if left.tpe <:< TypeRepr.of[Boolean] && right.tpe <:< TypeRepr.of[Boolean] => // OR (decodeTerm[Boolean](left, definitions), decodeTerm[Boolean](right, definitions)) match - case (Right(true), _) => Right(true) - case (_, Right(true)) => Right(true) + case (Right(true), _) => Right(true) + case (_, Right(true)) => Right(true) case (Right(leftValue), Right(rightValue)) => Right(leftValue || rightValue) - case (leftResult, rightResult) => Left(DecodingFailure.OrNotInlined(leftResult, rightResult)) + case (leftResult, rightResult) => Left(DecodingFailure.OrNotInlined(leftResult, rightResult)) case Apply(Select(left, "&&"), List(right)) if left.tpe <:< TypeRepr.of[Boolean] && right.tpe <:< TypeRepr.of[Boolean] => // AND (decodeTerm[Boolean](left, definitions), decodeTerm[Boolean](right, definitions)) match - case (Right(false), _) => Right(false) - case (_, Right(false)) => Right(false) + case (Right(false), _) => Right(false) + case (_, Right(false)) => Right(false) case (Right(leftValue), Right(rightValue)) => Right(leftValue && rightValue) - case (leftResult, rightResult) => Left(DecodingFailure.AndNotInlined(leftResult, rightResult)) + case (leftResult, rightResult) => Left(DecodingFailure.AndNotInlined(leftResult, rightResult)) case _ => Left(DecodingFailure.Unknown) @@ -324,4 +323,4 @@ class ReflectUtil[Q <: Quotes & Singleton](using val _quotes: Q): Left(DecodingFailure.StringPartsNotInlined(leftResult +: rparts)) case (leftResult, rightResult) => Left(DecodingFailure.StringPartsNotInlined(List(leftResult, rightResult))) - case _ => Left(DecodingFailure.Unknown) \ No newline at end of file + case _ => Left(DecodingFailure.Unknown) diff --git a/main/src/io/github/iltotore/iron/macros/package.scala b/main/src/io/github/iltotore/iron/macros/package.scala index 61a797e0..2effdd24 100644 --- a/main/src/io/github/iltotore/iron/macros/package.scala +++ b/main/src/io/github/iltotore/iron/macros/package.scala @@ -70,8 +70,8 @@ def compileTimeError(msg: String)(using Quotes): Nothing = |----------------------------------------------------------------------------""".stripMargin ) -inline def isIronType[T, C]: Boolean = ${isIronTypeImpl[T, C]} -def isIronTypeImpl[T : Type, C : Type](using Quotes): Expr[Boolean] = +inline def isIronType[T, C]: Boolean = ${ isIronTypeImpl[T, C] } +def isIronTypeImpl[T: Type, C: Type](using Quotes): Expr[Boolean] = import quotes.reflect.* val ironType = TypeRepr.of[IronType] diff --git a/main/test/src/io/github/iltotore/iron/testing/AnySuite.scala b/main/test/src/io/github/iltotore/iron/testing/AnySuite.scala index 3a61e78f..6e435334 100644 --- a/main/test/src/io/github/iltotore/iron/testing/AnySuite.scala +++ b/main/test/src/io/github/iltotore/iron/testing/AnySuite.scala @@ -12,8 +12,8 @@ object AnySuite extends TestSuite: test("false") - Dummy.assertNotRefine[False] test("describedAs"): - test - Dummy.assertRefine[True DescribedAs "test"] - test - Dummy.assertNotRefine[False DescribedAs "test"] + test - Dummy.assertRefine[DescribedAs[True, "test"]] + test - Dummy.assertNotRefine[DescribedAs[False, "test"]] test("not"): test - Dummy.assertRefine[Not[False]] diff --git a/sandbox/src/.gitkeep b/sandbox/src/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/zio/src/io/github/iltotore/iron/zio.scala b/zio/src/io/github/iltotore/iron/zio.scala index 296002bd..f8a92ba2 100644 --- a/zio/src/io/github/iltotore/iron/zio.scala +++ b/zio/src/io/github/iltotore/iron/zio.scala @@ -16,7 +16,6 @@ object zio extends RefinedTypeOpsZio: Validation.fromPredicateWith(constraint.message)(value.asInstanceOf[A :| C])(constraint.test(_)) extension [F[+_], A](wrapper: F[A]) - inline def refineAllValidation[C](using forEach: ForEach[F], inline constraint: Constraint[A, C]): Validation[InvalidValue[A], F[A :| C]] = forEach.forEach(wrapper): value => Validation.fromPredicateWith[InvalidValue[A], A :| C](InvalidValue(value, constraint.message))(value.assume[C])(constraint.test(_)) @@ -32,10 +31,14 @@ object zio extends RefinedTypeOpsZio: (value: A).refineValidation[C2].map(_.assumeFurther[C1]) extension [F[+_], A, C1](wrapper: F[A :| C1]) - - inline def refineAllFurtherValidation[C2](using forEach: ForEach[F], inline constraint: Constraint[A, C2]): Validation[InvalidValue[A], F[A :| (C1 & C2)]] = + inline def refineAllFurtherValidation[C2](using + forEach: ForEach[F], + inline constraint: Constraint[A, C2] + ): Validation[InvalidValue[A], F[A :| (C1 & C2)]] = forEach.forEach(wrapper): value => - Validation.fromPredicateWith[InvalidValue[A], A :| (C1 & C2)](InvalidValue(value, constraint.message))(value.assume[C1 & C2])(constraint.test(_)) + Validation.fromPredicateWith[InvalidValue[A], A :| (C1 & C2)](InvalidValue(value, constraint.message))(value.assume[C1 & C2])( + constraint.test(_) + ) extension [A, C, T](ops: RefinedTypeOps[A, C, T]) /** @@ -47,7 +50,6 @@ object zio extends RefinedTypeOpsZio: Validation.fromPredicateWith(ops.rtc.message)(value)(ops.rtc.test(_)).asInstanceOf[Validation[String, T]] extension [A, C, T](ops: RefinedTypeOps[A, C, T]) - /** * Refine the given values applicatively at runtime, resulting in a [[Validation]]. * diff --git a/zio/test/src/io/github/iltotore/iron/ZIOSuite.scala b/zio/test/src/io/github/iltotore/iron/ZIOSuite.scala index e8459553..f90b68f4 100644 --- a/zio/test/src/io/github/iltotore/iron/ZIOSuite.scala +++ b/zio/test/src/io/github/iltotore/iron/ZIOSuite.scala @@ -30,24 +30,33 @@ object ZIOSuite extends TestSuite: test("validation"): test - assert(valid.refineAllValidation[Positive] == ZValidation.Success(Chunk.empty, Chunk.from(valid))) - test - assert(invalid.refineAllValidation[Positive] == ZValidation.Failure(Chunk.empty, NonEmptyChunk( - InvalidValue(-2, "Should be strictly positive"), - InvalidValue(-3, "Should be strictly positive") - ))) - + test - assert(invalid.refineAllValidation[Positive] == ZValidation.Failure( + Chunk.empty, + NonEmptyChunk( + InvalidValue(-2, "Should be strictly positive"), + InvalidValue(-3, "Should be strictly positive") + ) + )) + test("newtype"): test - assert(Temperature.validationAll(valid) == ZValidation.Success(Chunk.empty, Chunk.from(valid))) - test - assert(Temperature.validationAll(invalid) == ZValidation.Failure(Chunk.empty, NonEmptyChunk( - InvalidValue(-2, "Should be strictly positive"), - InvalidValue(-3, "Should be strictly positive") - ))) - + test - assert(Temperature.validationAll(invalid) == ZValidation.Failure( + Chunk.empty, + NonEmptyChunk( + InvalidValue(-2, "Should be strictly positive"), + InvalidValue(-3, "Should be strictly positive") + ) + )) + test("furtherValidation"): val furtherValid = List(2, 4, 6).refineAllUnsafe[Positive] val furtherInvalid = List(1, 2, 3).refineAllUnsafe[Positive] - + test - assert(furtherValid.refineAllFurtherValidation[Even] == ZValidation.Success(Chunk.empty, Chunk.from(furtherValid))) - test - assert(furtherInvalid.refineAllFurtherValidation[Even] == ZValidation.Failure(Chunk.empty, NonEmptyChunk( - InvalidValue(1, "Should be a multiple of 2"), - InvalidValue(3, "Should be a multiple of 2") - ))) \ No newline at end of file + test - assert(furtherInvalid.refineAllFurtherValidation[Even] == ZValidation.Failure( + Chunk.empty, + NonEmptyChunk( + InvalidValue(1, "Should be a multiple of 2"), + InvalidValue(3, "Should be a multiple of 2") + ) + ))