-
Notifications
You must be signed in to change notification settings - Fork 14
/
Result.scala
250 lines (229 loc) · 8.53 KB
/
Result.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
/*
* Copyright 2020 Parsley Contributors <https://github.com/j-mie6/Parsley/graphs/contributors>
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package parsley
import scala.annotation.nowarn
import scala.util.{Failure => TFailure, Success => TSuccess, Try}
import scala.util.hashing.MurmurHash3
/** This trait represents the result of a parser.
*
* Either a `Success[A]` or a `Failure`.
*
* @tparam A the type of expected success result.
*/
sealed abstract class Result[+Err, +A] {
/** Returns the result of applying `ferr` to this result's error if this is a `Failure` or `fa` to the result stored in the `Success` otherwise.
*
* @param ferr the function to apply if this is a `Failure`.
* @param fa the function to apply if this is a `Success`.
* @return the results of applying the function
* @since 1.7.0
*/
def fold[B](ferr: Err => B, fa: A => B): B = this match {
case Success(x) => fa(x)
case Failure(msg) => ferr(msg.asInstanceOf[Err]) //FIXME: remove in 5.0
}
/** Executes the procedure `f` if this is a `Success`. Otherwise, do nothing.
*
* This is equivalent to:
* {{{
* result match {
* case Success(x) => f(x)
* case _ => ()
* }
* }}}
*
* @param f The side-effecting function to execute.
* @since 1.7.0
*/
def foreach[U](f: A => U): Unit = this match {
case Success(x) => f(x): @nowarn
case _ => ()
}
/** Returns the successful value within the result.
*
* This is equivalent to:
* {{{
* result match {
* case Success(x) => x
* case _ => throw new Exception
* }
* }}}
*
* @note the result must not be a failure.
* @throws java.util.NoSuchElementException if the result is a failure.
* @since 1.7.0
*/
def get: A
/** Returns the value from this `Success` or the result of evaluating `default` if this is a `Failure`.
*
* @since 1.7.0
*/
def getOrElse[B >: A](default: =>B): B = orElse(Success(default)).get
/** Returns this result if it is a `Success`, otherwise return the result of evaluating `alternative`.
*
* @since 1.7.0
*/
def orElse[B >: A, Errʹ >: Err](alternative: =>Result[Errʹ, B]): Result[Errʹ, B] = this match {
case Success(_) => this
case _ => alternative
}
/** Returns `true` if this result is a `Success` and its value is equal to `elem` (as determined by `==`),
* returns `false` otherwise.
*
* @param elem the element to test.
* @return `true` if this is a `Success` value equal to `elem`.
* @since 1.7.0
*/
def contains[B >: A](elem: B): Boolean = exists(_ == elem)
/** Returns `true` if this result is a `Failure` or returns the result of the application of
* the given predicate to the `Success` value.
*
* @since 1.7.0
*/
def forall(f: A => Boolean): Boolean = this match {
case Success(x) => f(x)
case _ => true
}
/** Returns `false` if `Failure` or returns the result of the application of
* the given predicate to the `Success` value.
*
* @since 1.7.0
*/
def exists(p: A => Boolean): Boolean = this match {
case Success(x) => p(x)
case _ => false
}
/** Returns the result of applying `f` to this result if it is a success. Returns
* a failure if this result is a failure. Differs from `map` as `f` returns a result
* instead of just a value.
*
* @since 1.7.0
*/
def flatMap[B, Errʹ >: Err](f: A => Result[Errʹ, B]): Result[Errʹ, B] = this match {
case Success(x) => f(x)
case _ => this.asInstanceOf[Result[Err, B]]
}
/** Returns the nested result if this result is a success, otherwise return this failure.
*
* Equivalent to `flatMap(identity[Result[Errʹ, B]])`.
*
* @since 1.7.0
*/
def flatten[B, Errʹ >: Err](implicit ev: A <:< Result[Errʹ, B]): Result[Errʹ, B] = flatMap(ev)
/** Returns a `Success` containing the result of applying `f` to this result's value if
* this is a success. Otherwise, returns a failure.
*
* @since 1.7.0
*/
def map[B](f: A => B): Result[Err, B] = this match {
case Success(x) => Success(f(x))
case _ => this.asInstanceOf[Result[Err, B]]
}
/** Returns `Success` with the existing value of `Success` if this is a `Success`
* and the given predicate `p` holds for the right value,
* or `Failure(msg)` if this is a `Success` and the given predicate `p` does not hold for the right value,
* or `Failure` with the existing value of `Failure` if this is a `Failure`.
*
* @since 1.7.0
*/
def filterOrElse[Errʹ >: Err](p: A => Boolean, msg: =>Errʹ): Result[Errʹ, A] = this match {
case Success(x) if !p(x) => Failure(msg)
case _ => this
}
/** Returns a `Seq` containing the `Success` value if it exists or an empty `Seq` if this is a `Failure`.
*
* @since 1.7.0
*/
def toSeq: Seq[A] = this match {
case Success(x) => Seq(x)
case _ => Seq.empty
}
/** Returns a `Some` containing the `Success` value if it exists or a `None` if this is a `Failure`.
*
* @since 1.7.0
*/
def toOption: Option[A] = this match {
case Success(x) => Some(x)
case _ => None
}
/** Converts the `Result` into a `Try` where `Failure` maps to a plain `Exception`.
*
* @since 1.7.0
*/
def toTry: Try[A] = this match {
case Success(x) => TSuccess(x)
case Failure(msg) => TFailure(new Exception(s"ParseError: $msg"))
}
/** Converts the `Result` into a `Either` where `Failure` maps to a `Left[Err]`.
*
* @since 1.7.0
*/
def toEither: Either[Err, A] = this match {
case Success(x) => Right(x)
case Failure(msg) => Left(msg.asInstanceOf[Err]) // FIXME: remove in 5.0
}
/** Returns `true` if this is a `Success`, `false` otherwise.
*
* @since 1.7.0
*/
def isSuccess: Boolean
/** Returns `true` if this is a `Failure`, `false` otherwise.
*
* @since 1.7.0
*/
def isFailure: Boolean
}
/** This class is used for when a parser succeeds, and contains its result.
*
* @param x the result value of the successful parse.
* @tparam A the type of expected success result.
*/
case class Success[A](x: A) extends Result[Nothing, A] {
/** @inheritdoc */
override def isSuccess: Boolean = true
/** @inheritdoc */
override def isFailure: Boolean = false
/** @inheritdoc */
override def get: A = x
}
/** This class is used for a parser failure, and contains the error message.
*
* @tparam Err the type of the error message generated by the failing parse.
* @param _msg the error message reported by the parser (passed lazily).
*/
class Failure[Err] private [parsley] (_msg: =>Err) extends Result[Err, Nothing] with Product with Serializable {
lazy val msg: Err = _msg
/** @inheritdoc */
override def isSuccess: Boolean = false
/** @inheritdoc */
override def isFailure: Boolean = true
/** @inheritdoc */
override def get: Nothing = throw new NoSuchElementException("get called on Failure") // scalastyle:ignore throw
// We are normally given everything below, but ideally we want to make error generation lazy
// $COVERAGE-OFF$
override def toString: String = s"Failure($msg)"
override def hashCode: Int = MurmurHash3.productHash(this)
override def canEqual(x: Any): Boolean = x.isInstanceOf[Failure[_]]
override def productPrefix: String = "Failure"
override def productArity: Int = 1
override def productElement(idx: Int): Any = {
if (idx != 0) throw new IndexOutOfBoundsException("Failure only has arity 1") else msg // scalastyle:ignore throw
}
override def equals(x: Any): Boolean = x != null && (x match {
case x: Failure[_] => x.msg == msg
case _ => false
})
def copy(msg: =>Err = this.msg): Failure[Err] = new Failure(msg)
// $COVERAGE-ON$
}
// $COVERAGE-OFF$
object Failure {
def apply[Err](msg: =>Err): Failure[Err] = new Failure(msg)
def unapply[Err](x: Failure[Err]): Some[Err] = Some(x.msg)
def andThen[A, Err](f: Failure[Err] => A): Err => A = msg => f(Failure(msg))
def compose[A, Err](f: A => Err): A => Failure[Err] = msg => Failure(f(msg))
}
// $COVERAGE-ON$