Skip to content

Commit

Permalink
feat: Add support for PureConfig (#264)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kevin Chuang authored Aug 30, 2024
1 parent a617545 commit 555940c
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ ivy"io.github.iltotore::iron:version"
| iron-decline | ✔️ | ✔️ | ✔️ |
| iron-doobie | ✔️ |||
| iron-jsoniter | ✔️ | ✔️ | ✔️ |
| iron-pureconfig | ✔️ | ❌️ | ❌️ |
| iron-scalacheck | ✔️ | ✔️ ||
| iron-skunk | ✔️ | ✔️ | ✔️ |
| iron-upickle | ✔️ | ✔️ | ✔️ |
Expand Down
14 changes: 13 additions & 1 deletion build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ object docs extends BaseModule {
def artifactName = "iron-docs"

val modules: Seq[ScalaModule] =
Seq(main, cats, circe, decline, doobie, upickle, ciris, jsoniter, scalacheck, skunk, upickle, zio, zioJson)
Seq(main, cats, circe, decline, doobie, upickle, ciris, jsoniter, pureconfig, scalacheck, skunk, upickle, zio, zioJson)

def docSources = T.sources {
T.traverse(modules)(_.docSources)().flatten
Expand Down Expand Up @@ -138,6 +138,7 @@ object docs extends BaseModule {
".*com.monovore.decline.*" -> ("scaladoc3", "https://javadoc.io/doc/com.monovore/decline_3/latest/"),
".*doobie.*" -> ("scaladoc3", "https://www.javadoc.io/doc/org.tpolecat/doobie-core_3/latest/"),
".*com.github.plokhotnyuk.jsoniter_scala.core.*" -> ("scaladoc3", "https://www.javadoc.io/doc/com.github.plokhotnyuk.jsoniter-scala/jsoniter-scala-core_3/latest/"),
".*pureconfig.*" -> ("scaladoc3", "https://www.javadoc.io/doc/com.github.pureconfig/pureconfig-core_3/latest/index.html"),
".*io.bullet.borer.*" -> ("scaladoc3", "https://javadoc.io/doc/io.bullet/borer-core_3/latest/"),
".*org.scalacheck.*" -> ("scaladoc3", "https://javadoc.io/doc/org.scalacheck/scalacheck_3/latest/"),
".*skunk.*" -> ("scaladoc3", "https://javadoc.io/doc/org.tpolecat/skunk-docs_3/latest/"),
Expand Down Expand Up @@ -479,3 +480,14 @@ object decline extends SubModule {

object native extends NativeCrossModule
}

object pureconfig extends SubModule {

def artifactName = "iron-pureconfig"

def ivyDeps = Agg(
ivy"com.github.pureconfig::pureconfig-core::0.17.7"
)

object test extends Tests
}
59 changes: 59 additions & 0 deletions docs/_docs/modules/pureconfig.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
title: "PureConfig Support"
---

# PureConfig Support

This module provides refined types ConfigReader instances for [PureConfig](https://pureconfig.github.io/).

## Dependency

SBT:

```scala
libraryDependencies += "io.github.iltotore" %% "iron-pureconfig" % "version"
```

Mill:

```scala
ivy"io.github.iltotore::iron-pureconfig:version"
```

### Following examples' dependencies

SBT:

```scala
libraryDependencies += "com.github.pureconfig" %% "pureconfig-core" % "0.17.7"
```

Mill:

```scala
ivy"com.github.pureconfig::pureconfig-core::0.17.7"
```

## ConfigReader instances

Iron provides `ConfigReader` instances for refined types:

```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
```
1 change: 1 addition & 0 deletions docs/sidebar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ subsection:
- page: modules/decline.md
- page: modules/doobie.md
- page: modules/jsoniter.md
- page: modules/pureconfig.md
- page: modules/skunk.md
- page: modules/scalacheck.md
- page: modules/upickle.md
Expand Down
30 changes: 30 additions & 0 deletions pureconfig/src/io/github/iltotore/iron/pureconfig.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.github.iltotore.iron

import _root_.pureconfig.ConfigReader
import _root_.pureconfig.error.FailureReason

object pureconfig:
final case class RefinedConfigError(description: String) extends FailureReason

/**
* A [[ConfigReader]] for refined types. Decodes to the underlying type then checks the constraint.
*
* @param reader the [[ConfigReader]] of the underlying type
* @param constraint the [[Constraint]] implementation to test the decoded value
*/
inline given [A, C](using inline reader: ConfigReader[A], inline constraint: Constraint[A, C]): ConfigReader[A :| C] =
reader
.emap: value =>
value
.refineEither[C]
.left
.map(RefinedConfigError(_))

/**
* A [[ConfigReader]] for new types. Decodes to the underlying type then checks the constraint
* @param mirror the mirror of the [[RefinedTypeOps.Mirror]]
* @param reader the [[ConfigReader]] of the underlying type
*/
inline given [A](using mirror: RefinedTypeOps.Mirror[A], reader: ConfigReader[mirror.IronType]): ConfigReader[A] =
reader.asInstanceOf[ConfigReader[A]]
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.github.iltotore.iron

import _root_.pureconfig.ConfigReader
import _root_.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
18 changes: 18 additions & 0 deletions pureconfig/test/src/io/github/iltotore/iron/PureConfigSuite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.github.iltotore.iron

import _root_.pureconfig.ConfigSource
import _root_.pureconfig.generic.derivation.default.*
import utest.*

object PureConfigSuite extends TestSuite:

val tests: Tests = Tests:

test("reader"):
test("ironType"):
test("success") - assert(ConfigSource.string("{ username: admin }").load[IronTypeConfig] == Right(IronTypeConfig("admin")))
test("failure") - assert(ConfigSource.string("{ username: a }").load[IronTypeConfig].isLeft)

test("newType"):
test("success") - assert(ConfigSource.string("{ username: admin }").load[NewTypeConfig] == Right(NewTypeConfig(Username("admin"))))
test("failure") - assert(ConfigSource.string("{ username: a }").load[NewTypeConfig].isLeft)

0 comments on commit 555940c

Please sign in to comment.