forked from http4s/http4s
/
ArgonautInstances.scala
118 lines (98 loc) · 4.58 KB
/
ArgonautInstances.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
package org.http4s
package argonaut
import _root_.argonaut.{DecodeResult => ArgDecodeResult, _}
import _root_.argonaut.Argonaut._
import _root_.argonaut.JawnParser.facade
import cats.effect.Sync
import org.http4s.headers.`Content-Type`
import jawn.JawnInstances
import org.typelevel.jawn.ParseException
import org.http4s.argonaut.ArgonautInstances.DecodeFailureMessage
trait ArgonautInstances extends JawnInstances {
implicit def jsonDecoder[F[_]: Sync]: EntityDecoder[F, Json] =
jawnDecoder
protected def jsonDecodeError: (Json, DecodeFailureMessage, CursorHistory) => DecodeFailure =
ArgonautInstances.defaultJsonDecodeError
def jsonOf[F[_]: Sync, A](implicit decoder: DecodeJson[A]): EntityDecoder[F, A] =
jsonDecoder[F].flatMapR { json =>
decoder
.decodeJson(json)
.fold(
(message, history) => DecodeResult.failure(jsonDecodeError(json, message, history)),
DecodeResult.success(_)
)
}
protected def defaultPrettyParams: PrettyParams = PrettyParams.nospace
implicit def jsonEncoder[F[_]]: EntityEncoder[F, Json] =
jsonEncoderWithPrettyParams[F](defaultPrettyParams)
def jsonEncoderWithPrettyParams[F[_]](prettyParams: PrettyParams): EntityEncoder[F, Json] =
EntityEncoder
.stringEncoder(Charset.`UTF-8`)
.contramap[Json](prettyParams.pretty)
.withContentType(`Content-Type`(MediaType.application.json))
def jsonEncoderOf[F[_], A](implicit encoder: EncodeJson[A]): EntityEncoder[F, A] =
jsonEncoderWithPrinterOf(defaultPrettyParams)
def jsonEncoderWithPrinterOf[F[_], A](prettyParams: PrettyParams)(
implicit encoder: EncodeJson[A]): EntityEncoder[F, A] =
jsonEncoderWithPrettyParams[F](prettyParams).contramap[A](encoder.encode)
implicit val uriCodec: CodecJson[Uri] = CodecJson(
(uri: Uri) => Json.jString(uri.toString),
c =>
c.as[String]
.flatMap(str =>
Uri
.fromString(str)
.fold(err => ArgDecodeResult.fail(err.toString, c.history), ArgDecodeResult.ok))
)
implicit class MessageSyntax[F[_]: Sync](self: Message[F]) {
def decodeJson[A](implicit decoder: DecodeJson[A]): F[A] =
self.as(implicitly, jsonOf[F, A])
}
}
sealed abstract case class ArgonautInstancesBuilder private[argonaut] (
defaultPrettyParams: PrettyParams = PrettyParams.nospace,
jsonDecodeError: (Json, String, CursorHistory) => DecodeFailure =
ArgonautInstances.defaultJsonDecodeError,
jawnParseExceptionMessage: ParseException => DecodeFailure =
JawnInstances.defaultJawnParseExceptionMessage,
jawnEmptyBodyMessage: DecodeFailure = JawnInstances.defaultJawnEmptyBodyMessage
) { self =>
def withPrettyParams(pp: PrettyParams): ArgonautInstancesBuilder =
this.copy(defaultPrettyParams = pp)
def withJsonDecodeError(
f: (Json, String, CursorHistory) => DecodeFailure): ArgonautInstancesBuilder =
this.copy(jsonDecodeError = f)
def withParseExceptionMessage(f: ParseException => DecodeFailure): ArgonautInstancesBuilder =
this.copy(jawnParseExceptionMessage = f)
def withEmptyBodyMessage(df: DecodeFailure): ArgonautInstancesBuilder =
this.copy(jawnEmptyBodyMessage = df)
protected def copy(
defaultPrettyParams: PrettyParams = self.defaultPrettyParams,
jsonDecodeError: (Json, String, CursorHistory) => DecodeFailure = self.jsonDecodeError,
jawnParseExceptionMessage: ParseException => DecodeFailure = self.jawnParseExceptionMessage,
jawnEmptyBodyMessage: DecodeFailure = self.jawnEmptyBodyMessage
): ArgonautInstancesBuilder =
new ArgonautInstancesBuilder(
defaultPrettyParams,
jsonDecodeError,
jawnParseExceptionMessage,
jawnEmptyBodyMessage) {}
def build: ArgonautInstances = new ArgonautInstances {
override val defaultPrettyParams: PrettyParams = self.defaultPrettyParams
override val jsonDecodeError: (Json, String, CursorHistory) => DecodeFailure =
self.jsonDecodeError
override val jawnParseExceptionMessage: ParseException => DecodeFailure =
self.jawnParseExceptionMessage
override val jawnEmptyBodyMessage: DecodeFailure = self.jawnEmptyBodyMessage
}
}
object ArgonautInstances {
type DecodeFailureMessage = String
def withPrettyParams(pp: PrettyParams): ArgonautInstancesBuilder =
builder.withPrettyParams(pp)
val builder: ArgonautInstancesBuilder = new ArgonautInstancesBuilder() {}
private[argonaut] def defaultJsonDecodeError
: (Json, DecodeFailureMessage, CursorHistory) => DecodeFailure =
(json, message, history) =>
InvalidMessageBodyFailure(s"Could not decode JSON: $json, error: $message, cursor: $history")
}