This repository has been archived by the owner on Mar 11, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 53
/
Configured.scala
146 lines (125 loc) · 4.69 KB
/
Configured.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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//: ----------------------------------------------------------------------------
//: Copyright (C) 2015 Verizon. All Rights Reserved.
//:
//: Licensed under the Apache License, Version 2.0 (the "License");
//: you may not use this file except in compliance with the License.
//: You may obtain a copy of the License at
//:
//: http://www.apache.org/licenses/LICENSE-2.0
//:
//: Unless required by applicable law or agreed to in writing, software
//: distributed under the License is distributed on an "AS IS" BASIS,
//: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//: See the License for the specific language governing permissions and
//: limitations under the License.
//:
//: ----------------------------------------------------------------------------
package knobs
import scala.concurrent.duration.{Duration, FiniteDuration}
import cats.Monad
import cats.implicits._
/**
* The class of types that can be automatically and safely
* converted from a `CfgValue` to a destination type. If conversion
* fails because the types are not compatible, `None` is returned.
*/
trait Configured[A] {
def apply(v: CfgValue): Option[A]
}
object Configured {
// second parameter (of any non-Unit) is required to get around SAM-derived ambiguities
def apply[A](implicit A: Configured[A], T: Trivial): Configured[A] = {
val _ = T
A
}
def apply[A](f: CfgValue => Option[A]): Configured[A] = new Configured[A] {
def apply(v: CfgValue) = f(v)
}
implicit val configuredMonad: Monad[Configured] = new Monad[Configured] {
def pure[A](a: A) = new Configured[A] {
def apply(v: CfgValue) = Some(a)
}
def flatMap[A,B](ca: Configured[A])(f: A => Configured[B]) = new Configured[B] {
def apply(v: CfgValue) = ca(v).flatMap(f(_)(v))
}
// Adapted from https://github.com/circe/circe/blob/master/modules/core/shared/src/main/scala/io/circe/Decoder.scala#L868-L877
def tailRecM[A, B](a: A)(f: A => Configured[Either[A, B]]): Configured[B] = new Configured[B] {
@annotation.tailrec
private[this] def step(v: CfgValue, a: A): Option[B] = f(a)(v) match {
case None => None
case Some(Left(a1)) => step(v, a1)
case Some(Right(b)) => Some(b)
}
def apply(v: CfgValue): Option[B] = step(v, a)
}
}
implicit val configuredDuration: Configured[Duration] = new Configured[Duration]{
def apply(a: CfgValue) = a match {
case CfgDuration(b) => Some(b)
case _ => None
}
}
implicit val configuredFiniteDuration: Configured[FiniteDuration] = new Configured[FiniteDuration]{
def apply(a: CfgValue) = configuredDuration(a) collect {
case d: FiniteDuration => d
}
}
implicit val configuredValue: Configured[CfgValue] = new Configured[CfgValue] {
def apply(a: CfgValue) = Some(a)
}
implicit val configuredInt: Configured[Int] = new Configured[Int] {
def apply(a: CfgValue) = a match {
case CfgNumber(n) if (n % 1 == 0.0) => Some(n.toInt)
case _ => None
}
}
implicit val configuredDouble: Configured[Double] = new Configured[Double] {
def apply(a: CfgValue) = a match {
case CfgNumber(n) => Some(n)
case _ => None
}
}
implicit val configuredString: Configured[String] = new Configured[String] {
def apply(a: CfgValue) = a match {
case CfgText(s) => Some(s)
case _ => None
}
}
implicit val configuredBool: Configured[Boolean] = new Configured[Boolean] {
def apply(a: CfgValue) = a match {
case CfgBool(b) => Some(b)
case _ => None
}
}
implicit def configuredList[A](implicit A: Configured[A]): Configured[List[A]] =
new Configured[List[A]] {
def apply(v: CfgValue) = v match {
case CfgList(xs) => xs.traverse(A.apply)
case _ => None
}
}
implicit def configuredTuple2[A,B](
implicit A: Configured[A], B: Configured[B]
): Configured[(A, B)] = new Configured[(A, B)] {
def apply(v: CfgValue) = v match {
case CfgList(a :: b :: Nil) => (A(a), B(b)).mapN((_, _))
case _ => None
}
}
implicit def configuredTuple3[A,B,C](
implicit A: Configured[A], B: Configured[B], C: Configured[C]
): Configured[(A, B, C)] = new Configured[(A, B, C)] {
def apply(v: CfgValue) = v match {
case CfgList(a :: b :: c :: Nil) => (A(a), B(b), C(c)).mapN((_, _, _))
case _ => None
}
}
implicit def configuredTuple4[A,B,C,D](
implicit A: Configured[A], B: Configured[B], C: Configured[C], D: Configured[D]
): Configured[(A, B, C, D)] = new Configured[(A, B, C, D)] {
def apply(v: CfgValue) = v match {
case CfgList(a :: b :: c :: d :: Nil) => (A(a), B(b), C(c), D(d)).mapN((_, _, _, _))
case _ => None
}
}
}