-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from note/circe-integration2
Circe integration
- Loading branch information
Showing
23 changed files
with
184 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,17 @@ | ||
import Common._ | ||
|
||
lazy val root = (project in file(".")) | ||
lazy val miniRefined = (project in file("core")) | ||
.commonSettings("mini-refined") | ||
.settings( | ||
libraryDependencies ++= Dependencies.testDeps | ||
) | ||
|
||
lazy val circeIntegration = (project in file("circe-integration")) | ||
.commonSettings("mini-refined-circe") | ||
.settings( | ||
libraryDependencies ++= Dependencies.circe ++ Dependencies.testDeps | ||
) | ||
.dependsOn(miniRefined) | ||
|
||
// do not publish the root project | ||
publish / skip := true |
27 changes: 27 additions & 0 deletions
27
circe-integration/src/main/scala/pl/msitko/refined/circe/Codecs.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package pl.msitko.refined.circe | ||
|
||
import io.circe.{Decoder, Encoder} | ||
import pl.msitko.refined.Refined | ||
|
||
// TODO: Find a way to encode those codecs generically instead of defining them for each supported type | ||
// Things like the following are not getting picked up by implicits mechanism, i.e. they work only when given is called explicitly: | ||
// given [T : Decoder, P <: Refined.ValidateExprFor[T]]: Encoder[T Refined P] = ??? | ||
|
||
given intEncoder[P <: Refined.ValidateExprFor[Int]](using Encoder[Int]): Encoder[Int Refined P] = | ||
summon[Encoder[Int]].contramap(_.value) | ||
|
||
inline given intDecoder[P <: Refined.ValidateExprFor[Int]](using Decoder[Int]): Decoder[Int Refined P] = | ||
summon[Decoder[Int]].emap(v => Refined.refineV[P](v)) | ||
|
||
given stringEncoder[P <: Refined.ValidateExprFor[String]](using Encoder[String]): Encoder[String Refined P] = | ||
summon[Encoder[String]].contramap(_.value) | ||
|
||
inline given stringDecoder[P <: Refined.ValidateExprFor[String]](using Decoder[String]): Decoder[String Refined P] = | ||
summon[Decoder[String]].emap(v => Refined.refineV[P](v)) | ||
|
||
given listEncoder[T, P <: Refined.ValidateExprFor[List[Any]]](using Encoder[List[T]]): Encoder[List[T] Refined P] = | ||
summon[Encoder[List[T]]].contramap(_.value) | ||
|
||
inline given listDecoder[T, P <: Refined.ValidateExprFor[List[Any]]](using | ||
Decoder[List[T]]): Decoder[List[T] Refined P] = | ||
summon[Decoder[List[T]]].emap(v => Refined.refineV[T, P](v)) |
63 changes: 63 additions & 0 deletions
63
circe-integration/src/test/scala/pl/msitko/refined/circe/CodecsSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package pl.msitko.refined.circe | ||
|
||
import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} | ||
import io.circe.syntax.* | ||
import io.circe.{parser, Decoder, Encoder, Printer} | ||
import pl.msitko.refined.auto.* | ||
import pl.msitko.refined.compiletime.ValidateExprInt | ||
import pl.msitko.refined.compiletime.ValidateExprInt.GreaterThan | ||
import pl.msitko.refined.Refined | ||
|
||
final case class Library( | ||
name: String Refined StartsWith["lib"], | ||
version: Int Refined GreaterThan[10], | ||
dependencies: List[String] Refined Size[GreaterThan[1]]) | ||
|
||
class CodecsSpec extends munit.FunSuite: | ||
given enc: Encoder[Library] = deriveEncoder[Library] | ||
given dec: Decoder[Library] = deriveDecoder[Library] | ||
|
||
test("should roundtrip for a manually defined Encoder and Decoder for type Library") { | ||
val in = Library("libA", 23, List("depA", "depB")) | ||
val encoded = in.asJson.printWith(Printer.spaces2) | ||
val Right(decoded) = parser.parse(encoded).flatMap(_.as[Library]): @unchecked | ||
|
||
assertEquals(decoded, in) | ||
} | ||
|
||
test("decoder should fail for incorrect Library.name") { | ||
val in = """{ | ||
| "name" : "something", | ||
| "version" : 11, | ||
| "dependencies": ["depA", "depB"] | ||
|}""".stripMargin | ||
|
||
val Left(decodingError) = parser.parse(in).flatMap(_.as[Library]): @unchecked | ||
assertEquals( | ||
decodingError.getMessage, | ||
"DecodingFailure at .name: Validation of refined type failed: something.startWith(lib)") | ||
} | ||
|
||
test("decoder should fail for incorrect Library.version") { | ||
val in = """{ | ||
| "name" : "libA", | ||
| "version" : 7, | ||
| "dependencies": ["depA", "depB"] | ||
|}""".stripMargin | ||
|
||
val Left(decodingError) = parser.parse(in).flatMap(_.as[Library]): @unchecked | ||
assertEquals(decodingError.getMessage, "DecodingFailure at .version: Validation of refined type failed: 7 > 10") | ||
} | ||
|
||
test("decoder should fail for incorrect Library.dependencies") { | ||
val in = """{ | ||
| "name" : "libA", | ||
| "version" : 11, | ||
| "dependencies": ["depA"] | ||
|}""".stripMargin | ||
|
||
val Left(decodingError) = parser.parse(in).flatMap(_.as[Library]): @unchecked | ||
assertEquals( | ||
decodingError.getMessage, | ||
"DecodingFailure at .dependencies: Validation of refined type failed: list size doesn't hold predicate: 1 > 1") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
core/src/main/scala/pl/msitko/refined/runtime/ValidateExprList.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package pl.msitko.refined.runtime | ||
|
||
import compiletime.{constValue, erasedValue} | ||
import pl.msitko.refined.compiletime as CT | ||
import pl.msitko.refined.runtime as RT | ||
|
||
sealed trait ValidateExprList: | ||
def validate(v: List[_]): Option[String] | ||
|
||
object ValidateExprList: | ||
final case class Size(sizeIntValidator: RT.ValidateExprInt) extends ValidateExprList: | ||
def validate(v: List[_]): Option[String] = | ||
sizeIntValidator.validate(v.size).map(err => s"list size doesn't hold predicate: $err") | ||
|
||
inline def fromCompiletime[T <: CT.ValidateExprList]: ValidateExprList = | ||
inline erasedValue[T] match | ||
case _: CT.ValidateExprList.Size[t] => Size(RT.ValidateExprInt.fromCompiletime[t]) |
38 changes: 38 additions & 0 deletions
38
core/src/main/scala/pl/msitko/refined/runtime/ValidateExprString.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package pl.msitko.refined.runtime | ||
|
||
import compiletime.{constValue, erasedValue} | ||
import pl.msitko.refined.compiletime as CT | ||
|
||
sealed trait ValidateExprString: | ||
def validate(v: String): Option[String] | ||
|
||
object ValidateExprString: | ||
|
||
final case class And(a: ValidateExprString, b: ValidateExprString) extends ValidateExprString: | ||
def validate(v: String): Option[String] = a.validate(v).orElse(b.validate(v)) | ||
|
||
final case class Or(a: ValidateExprString, b: ValidateExprString) extends ValidateExprString: | ||
def validate(v: String): Option[String] = a.validate(v) match | ||
case Some(err) => | ||
b.validate(v) match | ||
case Some(err2) => Some(s"($err Or $err2)") | ||
case None => None | ||
case None => | ||
None | ||
|
||
final case class StartsWith(t: String) extends ValidateExprString: | ||
def validate(v: String): Option[String] = | ||
if v.startsWith(t) then None | ||
else Some(s"$v.startWith($t)") | ||
|
||
final case class EndsWith(t: String) extends ValidateExprString: | ||
def validate(v: String): Option[String] = | ||
if v.endsWith(t) then None | ||
else Some(s"$v.endsWith($t)") | ||
|
||
inline def fromCompiletime[T <: CT.ValidateExprString]: ValidateExprString = | ||
inline erasedValue[T] match | ||
case _: CT.ValidateExprString.And[a, b] => And(fromCompiletime[a], fromCompiletime[b]) | ||
case _: CT.ValidateExprString.Or[a, b] => Or(fromCompiletime[a], fromCompiletime[b]) | ||
case _: CT.ValidateExprString.StartsWith[t] => StartsWith(constValue[t]) | ||
case _: CT.ValidateExprString.EndsWith[t] => EndsWith(constValue[t]) |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters