-
Notifications
You must be signed in to change notification settings - Fork 1
/
CustomUsage.scala
124 lines (100 loc) · 3.88 KB
/
CustomUsage.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package free.validation
import cats.arrow.{NaturalTransformation => ~>}
import cats.data.{Coproduct, Const, Kleisli}
import cats.free.Inject
import cats.syntax.cartesian._
import free.validation.Algebra.{DefaultMarks, JsonLikeAlgebra}
import free.validation.Dsl.JsonLikeDsl
import play.api.libs.json._
import scala.Function.unlift
object Doc {
case class DocMark[A](documentation: String)
class DocDsl[M[_]](implicit I: Inject[DocMark, M]) {
def doc[A](documentation: String): M[A] = I.inj(DocMark(documentation))
}
object DocDsl {
implicit def injectDocDsl[N[_]](implicit I: Inject[DocMark, N]): DocDsl[N] = new DocDsl[N]
}
val nt = new ~>[DocMark, Kleisli[Option, ?, String]] {
def apply[A](mark: DocMark[A]): Kleisli[Option, A, String] = Kleisli(_ => None)
}
}
object CustomUsage {
def run(): Unit = {
import Doc._
type MyMarks[A] = Coproduct[DocMark, DefaultMarks, A]
type AL[T] = JsonLikeAlgebra[MyMarks, T]
val coreDsl: JsonLikeDsl[MyMarks] = implicitly; import coreDsl._
val docDsl: DocDsl[MyMarks] = implicitly; import docDsl._
case class Pet(name: String, weight: Int)
case class Person(name: String, age: Int, pet: Option[Pet])
implicit val petConfig: FreeIM[AL, Pet] =
(
(__ \ "name").as[String]() |@|
(__ \ "weight").as[Int]()
).imap(Pet.apply)(unlift(Pet.unapply))
val personConfig: FreeIM[AL, Person] =
(
(__ \ "name").as[String](nonEmpty, doc("here is my name")) |@|
(__ \ "age").as[Int](doc("that's the age")) |@|
(__ \ "pet").as[Option[Pet]]()
).imap(Person.apply)(unlift(Person.unapply))
val codec: Codec[Person, JsObject] =
personConfig.foldMap[Codec[?, JsObject]](Compile2JsCodec.compile[MyMarks](Doc.nt or Compile2JsCodec.defaultMarks))
val me = Person("Olivier", 25, Some(Pet("sansan", 10)))
val me2 = Person("Olivier", 25, None)
val me3 = Person("", 25, Some(Pet("sansan", 10)))
val json: JsObject = codec.writes(me)
val json2: JsObject = codec.writes(me2)
val json3: JsObject = codec.writes(me3)
val validated = codec.validate(json)
val validated2 = codec.validate(json2)
val validated3 = codec.validate(json3)
assert(json.toString ==
"""{"name":"Olivier","age":25,"pet":{"name":"sansan","weight":10}}""")
assert(json2.toString ==
"""{"name":"Olivier","age":25,"pet":null}""")
assert(json3.toString ==
"""{"name":"","age":25,"pet":{"name":"sansan","weight":10}}""")
assert(validated.toString ==
"""Success(Person(Olivier,25,Some(Pet(sansan,10))))""")
assert(validated2.toString ==
"""Success(Person(Olivier,25,None))""")
assert(validated3.toString ==
"""Failure(List((/name,ArrayBuffer(ValidationError(List(empty string),WrappedArray())))))""")
val docNT: DocMark ~> Const[Option[String], ?] =
new ~>[DocMark, Const[Option[String], ?]] {
def apply[A](m: DocMark[A]): Const[Option[String], A] = Const(Some(m.documentation))
}
def myMarksCompiler[A] = Compile2JsonSchema.compile[MyMarks, A](docNT or Compile2JsonSchema.defaultMarks) _
val jsonSchema: JsObject = myMarksCompiler(personConfig)
assert(Json.prettyPrint(jsonSchema) ==
"""{
| "schema" : {
| "type" : "object",
| "properties" : {
| "name" : {
| "type" : "string",
| "description" : "here is my name"
| },
| "age" : {
| "type" : "number",
| "description" : "that's the age"
| },
| "pet" : {
| "type" : "object",
| "properties" : {
| "name" : {
| "type" : "string"
| },
| "weight" : {
| "type" : "number"
| }
| }
| }
| }
| }
|}""".stripMargin
)
}
}