### Run the FormCats Example Source: https://github.com/iltotore/iron/blob/main/examples/formCats/README.md Execute this command in the Iron root directory to run the form/API example. Ensure you have Mill build tool installed. ```sh mill examples.formCats.run ``` -------------------------------- ### Run Cats Validation Example Source: https://github.com/iltotore/iron/blob/main/examples/catsValidation/src/io/github/iltotore/iron/catsValidation/README.md Use this command to execute the validation example. Ensure you are in the Iron root directory. ```sh mill examples.catsValidation.run ``` -------------------------------- ### Run the formZio Example Source: https://github.com/iltotore/iron/blob/main/examples/formZio/README.md Execute this command in the Iron root directory to run the formZio example. It utilizes the mill build tool to compile and launch the application. ```sh mill examples.formZio.run ``` -------------------------------- ### Run Borer Serialization Example Source: https://github.com/iltotore/iron/blob/main/examples/borerSerialization/README.md Execute the Borer serialization example using the provided Mill command from the Iron project root directory. ```sh mill examples.borerSerialization.run ``` -------------------------------- ### Example Dependencies for Borer (SBT) Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/borer.md Include borer-core and borer-derivation in your SBT project. ```scala libraryDependencies ++= Seq( "io.bullet" %% "borer-core" % "1.13.0", "io.bullet" %% "borer-derivation" % "1.13.0" ) ``` -------------------------------- ### Example Dependencies for Borer (Mill) Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/borer.md Include borer-core and borer-derivation in your Mill project. ```scala mvn"io.bullet::borer-core::1.13.0" mvn"io.bullet::borer-derivation::1.13.0" ``` -------------------------------- ### Example of full reason in error message Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/config.md This example shows the full reason format for compile-time constraint evaluation failures. It provides detailed information about non-inlined terms, which can be helpful for debugging custom constraints. ```scala -- Constraint Error -------------------------------------------------------- Cannot refine value at compile-time because the predicate cannot be evaluated. This is likely because the condition or the input value isn't fully inlined. To test a constraint at runtime, use one of the `refine...` extension methods. Inlined input: a Inlined condition: (((a.>(0.0): Boolean): Boolean).&&((a.<(100.0): Boolean)): Boolean) Message: Should be strictly positive & Should be less than 100 Reason: Non-inlined boolean and. The following patterns are evaluable at compile-time: - && - && false - false && Left member: Some arguments of `>` are not inlined: Arg 0: Term not inlined: a Right member: Some arguments of `<` are not inlined: Arg 0: Term not inlined: a --------------------------------------------------------------------------- ``` -------------------------------- ### Example of short error message Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/config.md This example demonstrates the short, one-line error message format. It's useful for IDE integrations like VSCode's Error Lens. ```scala val x: Int :| Positive = -5 ``` -------------------------------- ### Example of short reason in error message Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/config.md This example illustrates the concise reason format for compile-time constraint evaluation failures. It's useful when the predicate or input is not fully inlined. ```scala -- Constraint Error -------------------------------------------------------- Cannot refine value at compile-time because the predicate cannot be evaluated. This is likely because the condition or the input value isn't fully inlined. To test a constraint at runtime, use one of the `refine...` extension methods. Inlined input: a Inlined condition: (((a.>(0.0): Boolean): Boolean).&&((a.<(100.0): Boolean)): Boolean) Message: Should be strictly positive & Should be less than 100 Reason: - Term not inlined: a: - at main/src/io/github/iltotore/iron/constraint/any.scala:[2990..2995] - at main/src/io/github/iltotore/iron/macros/intersection.scala:[903..908] ---------------------------------------------------------------------------- ``` -------------------------------- ### Example of detailed error message Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/config.md This example shows the detailed error message format, providing more context for compile-time constraint violations. ```scala -- Constraint Error -------------------------------------------------------- Could not satisfy a constraint for type scala.Int. Value: -5 Message: Should be strictly positive ---------------------------------------------------------------------------- ``` -------------------------------- ### Example User Registration Request Source: https://github.com/iltotore/iron/blob/main/examples/formZio/README.md This JSON object represents a sample request body for user registration. It includes fields for name, password, and age, which are subject to specific validation rules. ```json { "name": "totore", "password": "abc123", "age": 18 } ``` -------------------------------- ### Install iron-scodec Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/scodec.md Add the iron-scodec dependency to your build.sbt file. Replace 'version' with the latest Iron release. ```scala libraryDependencies += "io.github.iltotore" %% "iron-scodec" % "version" ``` -------------------------------- ### Using Cats Syntax with Iron Types Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/cats.md Leverage Cats' typeclass instances and syntax with Iron types after importing `io.github.iltotore.iron.cats.given`. This example shows string concatenation and comparison. ```scala import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.all.* import io.github.iltotore.iron.cats.given val name1: String :| Alphanumeric = "Martin".refineUnsafe val name2: String :| Alphanumeric = "George" val age1: Int :| Greater[0] = 60 name1.show // Martin name1 |+| name2 // MartinGeorge age1 === 49 // false ``` -------------------------------- ### Add Decline Dependency (SBT) Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/decline.md Add the decline library to your SBT project if you are following the examples. ```scala libraryDependencies += "com.monovore" %% "decline" % "2.4.1" ``` -------------------------------- ### RuntimeConstraint Usage Example Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/constraint.md Demonstrates how to use `RuntimeConstraint` for runtime refinement, which offers advantages over `Constraint` by not requiring `inline` methods and reducing bytecode generation. ```scala def refineOption[A, C](value: A)(using constraint: RuntimeConstraint[A, C]): Option[A :| C] = Option.when(constraint.test(value))(value.asInstanceOf[A :| C]) refineOption[Int, Positive](5) //Some(5) refineOption[Int, Positive](-5) //None ``` -------------------------------- ### Create User with Refined Username and Password Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/refinement.md Use `refineEither` to validate username and password against defined types. This example demonstrates basic validation for user creation. ```scala import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.all.* type Username = DescribedAs[Alphanumeric, "Username should be alphanumeric"] type Password = DescribedAs[ Alphanumeric & MinLength[5] & Exists[Letter] & Exists[Digit], "Password should have at least 5 characters, be alphanumeric and contain at least one letter and one digit" ] case class User(name: String :| Username, password: String :| Password) def createUser(name: String, password: String): Either[String, User] = for validName <- name.refineEither[Username] validPassword <- password.refineEither[Password] yield User(validName, validPassword) createUser("Iltotore", "abc123") //Right(User("Iltotore", "abc123")) createUser("Iltotore", "abc") //Left("Password should have at least 5 characters, be alphanumeric and contain at least one letter and one digit") ``` -------------------------------- ### Accumulative User Creation with EitherNec Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/cats.md Use refineNec for accumulative error handling with EitherNec when creating a User. This example demonstrates basic constraints. ```scala import cats.data.EitherNec import cats.syntax.all.* import io.github.iltotore.iron.* import io.github.iltotore.iron.cats.* import io.github.iltotore.iron.constraint.all.* case class User(name: String :| Alphanumeric, age: Int :| Positive) def createUserAcc(name: String, age: Int): EitherNec[String, User] = ( name.refineNec[Alphanumeric], age.refineNec[Positive] ).parMapN(User.apply) createUserAcc("Iltotore", 18) //Right(User(Iltotore,18)) createUserAcc("Il_totore", 18) //Left(Chain(Should be alphanumeric)) createUserAcc("Il_totore", -18) //Left(Chain(Should be alphanumeric, Should be greater than 0)) ``` -------------------------------- ### Add Dynosaur Core Dependency (SBT) Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/dynosaur.md Add the dynosaur-core library to your SBT build for examples requiring Dynosaur core functionality. ```scala libraryDependencies += "org.systemfw" %% "dynosaur-core" % "0.7.1" ``` -------------------------------- ### Compile-Time Type Checking in Scala Source: https://github.com/iltotore/iron/blob/main/docs/_docs/overview.md Demonstrates Scala's static typing by showing a valid `User` instantiation with an integer age. This example highlights that basic type checking works as expected. ```scala User(1) //OK ``` -------------------------------- ### Add Decline Dependency (Mill) Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/decline.md Add the decline library to your Mill project if you are following the examples. ```scala mvn"com.monovore::decline::2.4.1" ``` -------------------------------- ### Create User with Granular Password Refinement Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/refinement.md Utilize `refineFurtherEither` to apply constraints sequentially and get specific error messages for each failed constraint. This allows for more detailed feedback on password validation. ```scala type Username = DescribedAs[Alphanumeric, "Username should be alphanumeric"] type Password = DescribedAs[ Alphanumeric & MinLength[5] & Exists[Letter] & Exists[Digit], "Password should have at least 5 characters, be alphanumeric and contain at least one letter and one digit" ] case class User(name: String :| Username, password: String :| Password) def createUser(name: String, password: String): Either[String, User] = for validName <- name.refineEither[Username] alphanumeric <- password.refineEither[Alphanumeric] minLength <- alphanumeric.refineFurtherEither[MinLength[5]] hasLetter <- minLength.refineFurtherEither[Exists[Letter]] validPassword <- hasLetter.refineFurtherEither[Exists[Digit]] yield User(validName, validPassword) createUser("Iltotore", "abc123") //Right(User("Iltotore", "abc123")) createUser("Iltotore", "abc1") //Left("Should have a minimum length of 5") createUser("Iltotore", "abcde") //Left("At least one element: (Should be a digit)") createUser("Iltotore", "abc123 ") //Left("Should be alphanumeric") ``` -------------------------------- ### Use GreaterEqual Constraint Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/constraint.md Shows how to use the `GreaterEqual` constraint with a type alias. This example highlights compile-time error checking. ```scala val x: Int :| GreaterEqual[0] = 1 //OK val y: Int :| GreaterEqual[0] = -1 //Compile-time error: (Should be greater than 0 | Should strictly equal to 0) ``` -------------------------------- ### Add Dynosaur Core Dependency (Mill) Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/dynosaur.md Add the dynosaur-core library to your Mill build for examples requiring Dynosaur core functionality. ```scala mvn"org.systemfw::dynosaur-core:0.7.1" ``` -------------------------------- ### Serialize and Deserialize Refined Types with uPickle Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/upickle.md Demonstrates how to use Iron's Writer/Reader instances for uPickle to serialize and deserialize refined types. Includes examples of successful serialization and deserialization, as well as how invalid data triggers exceptions. ```scala import upickle.default._ import io.github.iltotore.iron._ import io.github.iltotore.iron.constraint.all._ import io.github.iltotore.iron.upickle.given opaque type Username = String :| Alphanumeric object Username extends RefinedTypeOps[String, Alphanumeric, Username] opaque type Age = Int :| Positive object Age extends RefinedTypeOps[Int, Positive, Age] case class User(name: Username, age: Age) derives ReadWriter write(User("Iltotore", 19)) //{"name":"Iltotore","age":19} read[User]("{\"name\":\"Iltotore\",\"age\":19}") //User("Iltotore", 19) read[User]("{\"name\":\"Iltotore\",\"age\":-19}") //AbortException: Should be strictly positive read[User]("{\"name\":\"Il_totore\",\"age\":19}") //AbortException: Should be alphanumeric ``` -------------------------------- ### Use Int with Positive Constraint Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/constraint.md Demonstrates the usage of the `Positive` constraint with an `Int` type, including a compile-time error example. ```scala val x: Int :| Positive = 1 val y: Int :| Positive = -1 //Compile-time error: Should be strictly positive ``` -------------------------------- ### Example Bad Request Response Source: https://github.com/iltotore/iron/blob/main/examples/formCats/README.md This JSON object illustrates a typical response when a user registration request fails validation. It contains a list of error messages detailing the specific validation failures. ```json HTTP/1.1 400 Bad Request Date: Wed, 23 Nov 2022 07:27:06 GMT Connection: keep-alive Content-Type: application/json Content-Length: 133 { "messages": [ "Password must contain atleast a letter, a digit and have a length between 6 and 20", "Age should be strictly positive" ] } ``` -------------------------------- ### Implement Constraint with a Trait for Multiple Types Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/constraint.md Shows how to reduce boilerplate for constraints supporting multiple types by using a trait. This example defines a `PositiveConstraint` trait. ```scala trait PositiveConstraint[A] extends Constraint[A, Positive]: override inline def message: String = "Should be strictly positive" given PositiveConstraint[Int] with override inline def test(inline value: Int): Boolean = value > 0 given PositiveConstraint[Double] with override inline def test(inline value: Double): Boolean = value > 0.0 ``` -------------------------------- ### Add Skunk Core Dependency Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/skunk.md Add the skunk-core dependency to your SBT or Mill build file if you are following the examples. ```scala libraryDependencies += "org.tpolecat" %% "skunk-core" % "1.0.0-M12" ``` ```scala mvn"org.tpolecat::skunk-core::1.0.0-M12" ``` -------------------------------- ### Invalid Value Response Example Source: https://github.com/iltotore/iron/blob/main/examples/formZio/README.md This JSON output illustrates a typical response when an invalid value is submitted in a registration request. It highlights the validation error message returned to the client. ```json HTTP/1.1 400 Bad Request Date: Wed, 23 Nov 2022 07:27:06 GMT Connection: keep-alive Content-Type: application/json Content-Length: 133 { "message": [ "Password must contain atleast a letter, a digit and have a length between 6 and 20" ] } ``` -------------------------------- ### Newtype type safety example Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/newtypes.md Demonstrates how opaque newtypes prevent accidental mixing of domain types, even if they share the same base type and constraints. ```scala import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.numeric.Positive type Temperature = Temperature.T object Temperature extends RefinedType[Double, Positive] val x: Double :| Positive = 5 val temperature: Temperature = x //Error: Temperature expected, got Double :| Positive ``` -------------------------------- ### Transitive Implication Example Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/implication.md Demonstrates how transitivity allows casting from a more constrained type to a less constrained one. Ensure necessary imports are present. ```scala import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.numeric.Greater val x: Int :| Greater[5] = ??? val y: Int :| Greater[0] = x ``` -------------------------------- ### RuntimeConstraint in RefinedType Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/constraint.md Illustrates a simplified example of `RuntimeConstraint` usage within a `RefinedType` trait, demonstrating its role in runtime checking of values. ```scala trait RefinedType[A, C]: inline def rtc: RuntimeConstraint[A, C] = ??? def option(value: A) = Option.when(rtc.test(value))(value.asInstanceOf[T]) ``` -------------------------------- ### User Creation with ZIO Validation Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/zio.md Demonstrates creating a User with ZIO's `refineValidation` for accumulative error handling. Requires imports for ZIO Prelude, Iron, and ZIO extensions. ```scala import zio.prelude.Validation import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.all.* import io.github.iltotore.iron.zio.* type Username = DescribedAs[Alphanumeric, "Username should be alphanumeric"] type Age = DescribedAs[Positive, "Age should be positive"] case class User(name: String :| Username, age: Int :| Age) def createUser(name: String, age: Int): Validation[String, User] = Validation.validateWith( name.refineValidation[Username], age.refineValidation[Age] )(User.apply) createUser("Iltotore", 18) //Success(Chunk(),User(Iltotore,18)) createUser("Il_totore", 18) //Failure(Chunk(),NonEmptyChunk(Username should be alphanumeric)) createUser("Il_totore", -18) //Failure(Chunk(),NonEmptyChunk(Username should be alphanumeric, Age should be positive)) ``` -------------------------------- ### Add ZIO and ZIO Prelude Dependencies Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/zio.md Include ZIO and ZIO Prelude libraries for using validation features. ```scala libraryDependencies += "dev.zio" %% "zio" % "2.0.5" libraryDependencies += "dev.zio" %% "zio-prelude" % "1.0.0-RC16" ``` ```scala mvn"dev.zio::zio:2.0.5" pub "dev.zio::zio-prelude:1.0.0-RC16" ``` -------------------------------- ### Runtime Refinement with Temperature Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/newtypes.md Shows how to perform runtime refinement for the `Temperature` type using `applyUnsafe`, `option`, and `either` constructors. ```scala import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.numeric.Positive type Temperature = Temperature.T object Temperature extends RefinedType[Double, Positive] val unsafeRuntime: Temperature = Temperature.applyUnsafe(15) val option: Option[Temperature] = Temperature.option(15) val either: Either[String, Temperature] = Temperature.either(15) ``` -------------------------------- ### ConfigReader Instances for Refined Types Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/pureconfig.md Demonstrates how Iron provides ConfigReader instances for refined types, enabling direct configuration loading into types with constraints. Requires importing `io.github.iltotore.iron.pureconfig.given`. ```scala package io.github.iltotore.iron import pureconfig.ConfigReader import pureconfig.generic.derivation.default.* import io.github.iltotore.iron.constraint.all.* import io.github.iltotore.iron.pureconfig.given opaque type Username = String :| MinLength[5] object Username extends RefinedTypeOps[String, MinLength[5], Username] case class IronTypeConfig( username: String :| MinLength[5] ) derives ConfigReader case class NewTypeConfig( username: Username ) derives ConfigReader ``` -------------------------------- ### Define a refined subtype Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/newtypes.md Example of defining a new type that is a subtype of an existing refined type, using `RefinedSubtype`. ```scala import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.all.* type FirstName = FirstName.T object FirstName extends RefinedSubtype[String, ForAll[Letter]] ``` -------------------------------- ### Import Standard Numeric Constraints Source: https://github.com/iltotore/iron/blob/main/docs/_docs/getting-started.md Import standard number-related constraints from the `io.github.iltotore.iron.constraint.numeric` package to use in your project. ```scala import io.github.iltotore.iron.constraint.numeric.* ``` -------------------------------- ### Instantiate Temperature RefinedType Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/newtypes.md Demonstrates creating instances of the `Temperature` type. It shows direct instantiation and creation from an existing refined value. ```scala import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.numeric.Positive type Temperature = Temperature.T object Temperature extends RefinedType[Double, Positive] val temperature = Temperature(15) //Compiles println(temperature) //15 val positive: Double :| Positive = 15 val tempFromIron = Temperature(positive) //Compiles too ``` -------------------------------- ### Skunk Codec Instances for Refined Types Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/skunk.md Demonstrates how to define and use Skunk Codec instances for Iron's refined types, including refining codecs at usage sites and defining codecs for opaque types and case classes. ```scala import skunk.* import skunk.implicits.* import skunk.codec.all.* import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.all.* import io.github.iltotore.iron.skunk.* import io.github.iltotore.iron.skunk.given type Username = String :| Not[Blank] // refining a codec at usage site val a: Query[Void, Username] = sql"SELECT name FROM users".query(varchar.refined) // defining a codec for a refined opaque type type PositiveInt = PositiveInt.T object PositiveInt extends RefinedType[Int, Positive]: given codec: Codec[PositiveInt] = int4.refined[Positive].imap(assume)(_.value) // defining a codec for a refined case class final case class User(name: Username, age: PositiveInt) given Codec[User] = (varchar.refined[Not[Blank]] *: PositiveInt.codec).to[User] ``` -------------------------------- ### Add Circe Dependencies (SBT) Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/circe.md Include the necessary Circe core, generic, and parser modules in your SBT build. ```scala libraryDependencies += "io.circe" %% "circe-core" % "0.14.5" libraryDependencies += "io.circe" %% "circe-generic" % "0.14.5" libraryDependencies += "io.circe" %% "circe-parser" % "0.14.5" ``` -------------------------------- ### Add Doobie Core Dependency Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/doobie.md Add the Doobie core library to your build if you haven't already. Ensure compatible versions are used. ```scala libraryDependencies += "org.tpolecat" %% "doobie-core" % "1.0.0-RC10" ``` ```scala mvn"org.tpolecat::doobie-core::1.0.0-RC10" ``` -------------------------------- ### Add pureconfig-core Dependency Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/pureconfig.md Ensure pureconfig-core is available for using its configuration reading capabilities. ```scala libraryDependencies += "com.github.pureconfig" %% "pureconfig-core" % "0.17.7" ``` ```scala mvn"com.github.pureconfig::pureconfig-core::0.17.7" ``` -------------------------------- ### Compile-time equivalent of `assume` Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/refinement.md This is how the `assume` code compiles, showing that it leaves no overhead. ```scala val random: Int = scala.util.Random.nextInt(9)+1 val x: Int = random ``` -------------------------------- ### Deriving Typeclasses with RuntimeConstraint Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/constraint.md Shows how to derive typeclasses like `FromString` using `RuntimeConstraint`. This approach is recommended, especially with function values, to avoid excessive bytecode generation compared to using `Constraint` with `inline`. ```scala trait FromString[A]: def fromString(text: String): Either[String, A] given [A, C](using constraint: RuntimeConstraint[A, C], instanceA: FromString[A]): FromString[A :| C] = text => instanceA .fromString(text) .filterOrElse(constraint.test(_), constraint.message) .map(_.asInstanceOf[A :| C]) ``` -------------------------------- ### Add Iron Dynosaur Dependency (SBT) Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/dynosaur.md Add the iron-dynosaur library to your SBT build to use Dynosaur support. ```scala libraryDependencies += "io.github.iltotore" %% "iron-dynosaur" % "version" ``` -------------------------------- ### Define distinct newtypes for similar domain types Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/newtypes.md Illustrates defining separate opaque newtypes for Temperature and Moisture, both based on Double with Positive constraints, to maintain distinct domain types. ```scala import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.numeric.Positive type Temperature = Temperature.T object Temperature extends RefinedType[Double, Positive] type Moisture = Moisture.T object Moisture extends RefinedType[Double, Positive] ``` -------------------------------- ### Define and Use Constrained Types with Decline Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/decline.md Demonstrates defining options for constrained types like `Person` (String :| Not[Blank]) and refined opaque types like `PositiveInt`. Ensure necessary imports are included for iron and decline. ```scala import cats.implicits.* import com.monovore.decline.* import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.all.* import io.github.iltotore.iron.decline.given type Person = String :| Not[Blank] opaque type PositiveInt <: Int = Int :| Positive object PositiveInt extends RefinedTypeOps[Int, Positive, PositiveInt] object HelloWorld extends CommandApp( name = "hello-world", header = "Says hello!", main = { // Defining an option for a constrainted type val userOpt = Opts.option[Person]("target", help = "Person to greet.") .withDefault("world") // Defining an option for a refined opaque type val nOpt = Opts.option[PositiveInt]("quiet", help = "Number of times message is printed.") .withDefault(PositiveInt(1)) (userOpt, nOpt).mapN { (user, n) => (1 to n).map(_ => println(s"Hello $user!")) } } ) ``` -------------------------------- ### Typeclass derivation for opaque types using Mirror Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/newtypes.md Illustrates how to derive typeclasses for opaque newtypes by using the `RefinedType.Mirror` provided by Iron, similar to Scala 3's derivation. ```scala import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.numeric.Positive type Temperature = Temperature.T object Temperature extends RefinedType[Double, Positive] ``` ```scala import zio.json.* import io.github.iltotore.iron.* inline given[T](using mirror: RefinedType.Mirror[T], ev: JsonDecoder[mirror.IronType]): JsonDecoder[T] = ev.asInstanceOf[JsonDecoder[T]] ``` -------------------------------- ### Doobie Integration with Iron Refined Types Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/doobie.md Demonstrates how to use Iron's refined types (e.g., CountryCode, CountryName, Population) with Doobie for type-safe database operations. Imports for Doobie, its implicits, and Iron's given instances are required. ```scala import doobie.* import doobie.implicits.* import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.all.* import io.github.iltotore.iron.doobie.given type CountryCode = CountryCode.T object CountryCode extends RefinedType[Int, Positive] type CountryName = CountryName.T object CountryName extends RefinedType[String, Not[Blank]] type Population = Population.T object Population extends RefinedType[Int, Positive] //Refined columns of a table case class Country(code: CountryCode, name: CountryName, pop: Population) //Interpolation with refined values def biggerThan(minPop: Population) = sql""" select code, name, population from country where population > $minPop """.query[Country] ``` -------------------------------- ### Accumulative User Creation with Custom Messages Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/cats.md Define custom error messages for constraints using DescribedAs for accumulative error handling with EitherNec. ```scala import cats.data.EitherNec import cats.syntax.all.* import io.github.iltotore.iron.* import io.github.iltotore.iron.cats.* import io.github.iltotore.iron.constraint.all.* type Username = DescribedAs[Alphanumeric, "Username should be alphanumeric"] type Age = DescribedAs[Positive, "Age should be positive"] case class User(name: String :| Username, age: Int :| Age) def createUserAcc(name: String, age: Int): EitherNec[String, User] = ( name.refineNec[Username], age.refineNec[Age] ).parMapN(User.apply) createUserAcc("Iltotore", 18) //Right(User(Iltotore,18)) createUserAcc("Il_totore", 18) //Left(Chain(Username should be alphanumeric)) createUserAcc("Il_totore", -18) //Left(Chain(Username should be alphanumeric, Age should be positive)) ``` -------------------------------- ### Create User with Custom Error Messages Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/refinement.md Customize error messages for each refinement step using `.left.map` to provide user-friendly feedback. This enhances the clarity of validation failures. ```scala type Username = DescribedAs[Alphanumeric, "Username should be alphanumeric"] type Password = DescribedAs[ Alphanumeric & MinLength[5] & Exists[Letter] & Exists[Digit], "Password should have at least 5 characters, be alphanumeric and contain at least one letter and one digit" ] case class User(name: String :| Username, password: String :| Password) def createUser(name: String, password: String): Either[String, User] = for validName <- name.refineEither[Username] alphanumeric <- password.refineEither[Alphanumeric].left.map(_ => "Your password should be alphanumeric") minLength <- alphanumeric.refineFurtherEither[MinLength[5]].left.map(_ => "Your password should have a minimum length of 5") hasLetter <- minLength.refineFurtherEither[Exists[Letter]].left.map(_ => "Your password should contain at least a letter") validPassword <- hasLetter.refineFurtherEither[Exists[Digit]].left.map(_ => "Your password should contain at least a digit") yield User(validName, validPassword) createUser("Iltotore", "abc123") //Right(User("Iltotore", "abc123")) createUser("Iltotore", "abc1") //Left("Your password should have a minimum length of 5") createUser("Iltotore", "abcde") //Left("Your password should contain at least a digit") createUser("Iltotore", "abc123 ") //Left("Your password should be alphanumeric") ``` -------------------------------- ### Use Parameterized Greater Constraint Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/constraint.md Shows how to use the parameterized `Greater` constraint with a specific type parameter, demonstrating compile-time error checking. ```scala val x: Int :| Greater[5] = 6 val y: Int :| Greater[5] = 3 //Compile-time error: Should be greater than 5 ``` -------------------------------- ### Play JSON Encoding and Decoding with Iron Types Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/play-json.md Demonstrates how to encode and decode case classes containing Iron refined types using Play JSON's Writes and Reads instances. Ensure the necessary imports and givens are in scope. ```scala import play.api.libs.json.{Reads, Writes, Json} import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.all.* import io.github.iltotore.iron.playJson.given type Username = DescribedAs[Alphanumeric, "Username should be alphanumeric"] type Age = DescribedAs[Positive, "Age should be positive"] case class User(name: String :| Username, age: Int :| Age) //Encoding Json.stringify(Json.writes[User].writes(User("Iltotore", 8))) //{"name":"Iltotore", "age":18} //Decoding Json.fromJson[User](Json.parse("{\"name\":\"Iltotore\",\"age\":18}"))(Json.reads[User]) //JsSuccess(User(Iltotore,18),) ``` -------------------------------- ### Add ZIO Iron Dependency Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/zio.md Add the iron-zio dependency to your SBT or Mill build configuration. ```scala libraryDependencies += "io.github.iltotore" %% "iron-zio" % "version" ``` ```scala mvn"io.github.iltotore::iron-zio:version" ``` -------------------------------- ### Transforming to PureIntW Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/chimney.md Demonstrates transforming a RawInt into a PureIntW, utilizing a refined type alias for Int with a Pure constraint. ```scala type PureInt = PureInt.T object PureInt extends RefinedType[Int, Pure] final case class PureIntW(i: PureInt) RawInt(1).transformInto[PureIntW].i.value // 1 ``` -------------------------------- ### Encode and Decode Refined Types with Circe Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/circe.md Demonstrates encoding a case class with Iron's refined types to JSON and decoding JSON back into the case class. Ensure Circe and Iron Circe are in your dependencies and `io.github.iltotore.iron.circe.given` is imported. ```scala import io.circe.* import io.circe.parser.* import io.circe.syntax.* import io.circe.generic.auto.* import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.all.* import io.github.iltotore.iron.circe.given type Username = DescribedAs[Alphanumeric, "Username should be alphanumeric"] type Age = DescribedAs[Positive, "Age should be positive"] case class User(name: String :| Username, age: Int :| Age) //Encoding User("Iltotore", 8).asJson //{"name":"Iltotore", "age":18} //Decoding decode[User]("{\"name\":\"Iltotore\",\"age\":18}") //Right(User(Iltotore, 18)) ``` -------------------------------- ### Add Circe Dependencies (Mill) Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/circe.md Include the necessary Circe core, generic, and parser modules in your Mill build. ```scala mvn"io.circe::circe-core::0.14.5" pub ``` -------------------------------- ### Add Iron Dynosaur Dependency (Mill) Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/dynosaur.md Add the iron-dynosaur library to your Mill build to use Dynosaur support. ```scala mvn"io.github.iltotore::iron-dynosaur:version" ``` -------------------------------- ### Add Cats Core Dependency Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/cats.md Include the cats-core library in your build if you are using Cats features. ```scala libraryDependencies += "org.typelevel" %% "cats-core" % "2.8.0" ``` ```scala mvn"org.typelevel::cats-core::2.8.0" ``` -------------------------------- ### Chimney Transformer Instances for Iron Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/chimney.md Enables automatic derivation of Transformer instances for Iron's refined types. Demonstrates transforming a validated type to a raw type and handling partial transformations that fail validation. ```scala import io.scalaland.chimney.dsl.* import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.all.* import io.github.iltotore.iron.chimney.given final case class PositiveInt(i: Int :| Positive) final case class RawInt(i: Int) PositiveInt(1).transformInto[RawInt].i // 1 RawInt(-1).transformIntoPartial[PositiveInt].asErrorPathMessageStrings // List((i,Should be strictly positive)) RawInt(100).transformIntoPartial[PositiveInt] // Value(PositiveInt(100)) ``` -------------------------------- ### Using Refined Types with Compile-Time Verification Source: https://github.com/iltotore/iron/blob/main/README.md Demonstrates how to use Iron's refined types for compile-time verification of constraints. Ensure the input satisfies the 'Positive' constraint before compilation. ```scala import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.numeric.* def log(x: Double :| Positive): Double = Math.log(x) //Used like a normal `Double` log(1.0) //Automatically verified at compile time. log(-1.0) //Compile-time error: Should be strictly positive val runtimeValue: Double = ??? log(runtimeValue.refineUnsafe) //Explicitly refine your external values at runtime. runtimeValue.refineEither.map(log) //Use monadic style for functional validation runtimeValue.refineEither[Positive].map(log) //More explicitly ``` -------------------------------- ### Define GreaterEqual Constraint using Union Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/constraint.md Demonstrates how to define a `GreaterEqual` constraint by taking the union of `Greater` and `StrictEqual` constraints. ```scala type GreaterEqual[V] = Greater[V] | StrictEqual[V] ``` -------------------------------- ### Enable Borer Integration with Iron Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/borer.md Import `io.github.iltotore.iron.borer.given` to enable automatic Encoder/Decoder instances for iron's refined types. ```scala import io.github.iltotore.iron.borer.given ``` -------------------------------- ### Add Iron Decline Dependency (SBT) Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/decline.md Add the iron-decline library to your SBT project to use its features. ```scala libraryDependencies += "io.github.iltotore" %% "iron-decline" % "version" ``` -------------------------------- ### Accumulative Error Refinement with ZIO Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/refinement.md Leverage the ZIO module for accumulative refinement errors, returning a `Validation` type. This allows reporting all input errors without short-circuiting, ideal for complex forms. ```scala import zio.prelude.Validation import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.all.* import io.github.iltotore.iron.zio.* type Username = DescribedAs[Alphanumeric, "Username should be alphanumeric"] type Age = DescribedAs[Positive, "Age should be positive"] case class User(name: String :| Username, age: Int :| Age) def createUser(name: String, age: Int): Validation[String, User] = Validation.validateWith( name.refineValidation[Username], age.refineValidation[Age] )(User.apply) createUser("Iltotore", 18) //Success(Chunk(),User(Iltotore,18)) createUser("Il_totore", 18) //Failure(Chunk(),NonEmptyChunk(Username should be alphanumeric)) createUser("Il_totore", -18) //Failure(Chunk(),NonEmptyChunk(Username should be alphanumeric, Age should be positive)) ``` -------------------------------- ### Add iron-pureconfig Dependency Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/pureconfig.md Include the iron-pureconfig library in your SBT or Mill build configuration. ```scala libraryDependencies += "io.github.iltotore" %% "iron-pureconfig" % "version" ``` ```scala mvn"io.github.iltotore::iron-pureconfig:version" ``` -------------------------------- ### Explicit conversion using smart constructor Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/newtypes.md Demonstrates the need for explicit conversion using a smart constructor when assigning a refined type to an opaque newtype. ```scala import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.numeric.Positive type Temperature = Temperature.T object Temperature extends RefinedType[Double, Positive] val value: Double :| Positive = ??? val a: Temperature = value //Compile-time error val b: Temperature = Temperature(value) //OK ``` -------------------------------- ### Add Iron Circe Dependency (SBT) Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/circe.md Add the iron-circe module to your SBT dependencies to use Circe integration. ```scala libraryDependencies += "io.github.iltotore" %% "iron-circe" % "version" ``` -------------------------------- ### Add Jsoniter Scala Core and Macros Dependencies (SBT) Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/jsoniter.md Include Jsoniter Scala core and macro libraries in your SBT build for JSON processing. ```scala libraryDependencies += "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.19.1" libraryDependencies += "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.19.1" ``` -------------------------------- ### Add iron-zio-json Dependency Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/zio-json.md Include the iron-zio-json dependency in your SBT or Mill build configuration. ```scala libraryDependencies += "io.github.iltotore" %% "iron-zio-json" % "version" ``` ```scala mvn"io.github.iltotore::iron-zio-json:version" ``` -------------------------------- ### Instantiate FirstName RefinedType Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/newtypes.md Demonstrates creating an instance of the `FirstName` type, which uses the `Pure` constraint and thus has no runtime validation. ```scala import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.any.Pure type FirstName = FirstName.T object FirstName extends RefinedType[String, Pure] val firstName = FirstName("whatever") ``` -------------------------------- ### Add ZIO JSON Dependency Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/zio-json.md Include the zio-json dependency in your SBT or Mill build configuration if you haven't already. ```scala libraryDependencies += "dev.zio" %% "zio-json" % "0.3.0" ``` ```scala mvn"dev.zio::zio:0.3.0" ``` -------------------------------- ### Add Iron Jsoniter Dependency (SBT) Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/jsoniter.md Add the iron-jsoniter module to your SBT build to enable Jsoniter support. ```scala libraryDependencies += "io.github.iltotore" %% "iron-jsoniter" % "version" ``` -------------------------------- ### Define Between Constraint using Intersection Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/constraint.md Illustrates defining a `Between` constraint by intersecting `GreaterEqual` and `LessEqual` constraints. ```scala type Between[Min, Max] = GreaterEqual[Min] & LessEqual[Max] ``` -------------------------------- ### Define and Use Refined Types with Dynosaur Schema Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/dynosaur.md Demonstrates defining refined types using Iron and RefinedType, creating a case class, and deriving a Dynosaur Schema for it. This allows encoding and decoding data with validation. ```scala import io.github.iltotore.dynosaur.given import dynosaur.Schema import dynosaur.DynamoValue import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.all.* import software.amazon.awssdk.services.dynamodb.model.AttributeValue as JAttributeValue import java.util.Map as JMap import cats.syntax.all.* import java.util.Map as JMap import cats.syntax.all.* // defining a type with IronType type FirstName = String :| Not[Blank] // defining a type with RefinedType type LastName = LastName.T object LastName extends RefinedType[String, Not[Blank]] // defining a type with RefinedSubtype type Age = Age.T object Age extends RefinedSubtype[Int, Positive] // defining a case class that represents a decodable/encodable DynamoDB record final case class Person(firstName: FirstName, lastName: LastName, age: Age) object Person: // defining a Schema[Person] instance of the Person type given Schema[Person] = Schema.record: r => ( r("first_name", _.firstName), r("last_name", _.lastName), r("age", _.age) ).mapN(Person.apply) val validJDynamoValue: DynamoValue = DynamoValue( JAttributeValue.fromM( JMap.of( "first_name", JAttributeValue.fromS("iron"), "last_name", JAttributeValue.fromS("dynosaur"), "age", JAttributeValue.fromN("1") ) ) ) val invalidJDynamoValue: DynamoValue = DynamoValue( JAttributeValue.fromM( JMap.of( "first_name", JAttributeValue.fromS("iron"), "last_name", JAttributeValue.fromS("dynosaur"), "age", JAttributeValue.fromN("0") // 0 does not satisfy Positive constraint ) ) ) object DynosaurExample extends App: println(Schema[Person].read(Person.validJDynamoValue)) // Right(Person(firstName = "iron", lastName = "dynosaur", age = 1)) println(Schema[Person].read(Person.invalidJDynamoValue)) // Left(ReadError("Should be greater than 0")) ``` -------------------------------- ### Runtime Refinement with `refineUnsafe` Source: https://github.com/iltotore/iron/blob/main/docs/_docs/reference/refinement.md Use `refineUnsafe` to imperatively test constraints at runtime. It throws an `IllegalArgumentException` if the value fails the assertion. ```scala import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.string.* val runtimeString: String = ??? val username: String :| Alphanumeric = runtimeString val username: String :| Alphanumeric = runtimeString.refineUnsafe //or more explicitly, refineUnsafe[LowerCase]. ``` -------------------------------- ### Load Refined Types with Ciris ConfigDecoder Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/ciris.md Demonstrates loading refined types like Username and Password, along with a Secret type, from environment variables using Ciris and Iron's provided `ConfigDecoder` instances. ```scala import cats.syntax.all.* import ciris.* import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.all.* import io.github.iltotore.iron.cats.given import io.github.iltotore.iron.ciris.given type Username = String :| (Not[Blank] & MaxLength[32]) type Password = String :| (Not[Blank] & MinLength[9]) case class DatabaseConfig(username: Username, password: Secret[Password]) val databaseConfig: ConfigValue[Effect, DatabaseConfig] = ( env("DB_USERNAME").as[Username], env("DB_PASSWORD").as[Password].secret ).mapN(DatabaseConfig.apply) ``` -------------------------------- ### Add Iron Decline Dependency (Mill) Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/decline.md Add the iron-decline library to your Mill project to use its features. ```scala mvn"io.github.iltotore::iron-decline:version" ``` -------------------------------- ### Add Chimney Dependency Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/chimney.md Add the Chimney library to your SBT or Mill build configuration to use its features. ```scala libraryDependencies += "io.scalaland" %% "chimney" % "version" ``` ```scala mvn"io.scalaland::chimney:version" ``` -------------------------------- ### Add uPickle Dependency Source: https://github.com/iltotore/iron/blob/main/docs/_docs/modules/upickle.md Include the uPickle library in your build configuration if you intend to use its serialization capabilities. ```scala libraryDependencies += "com.lihaoyi" %% "upickle" % "3.1.3" ``` ```scala mvn"com.lihaoyi::upickle:3.1.3" ```