-
Notifications
You must be signed in to change notification settings - Fork 29
/
Using.scala
125 lines (112 loc) · 4.16 KB
/
Using.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
package com.thoughtworks.dsl
package keywords
import com.thoughtworks.dsl.Dsl
import com.thoughtworks.dsl.Dsl.{!!, Keyword}
import com.thoughtworks.dsl.keywords.Catch.{CatchDsl, DslCatch}
import scala.concurrent.{ExecutionContext, Future}
import scala.language.implicitConversions
import scala.util.control.NonFatal
/** This [[Using]] keyword automatically manage resources in [[scala.concurrent.Future]], [[domains.task.Task]],
* and other asynchrounous domains derived from `Future` or `Task`.
*
* @author 杨博 (Yang Bo)
* @see [[dsl]] for usage of this [[Using]] keyword in continuations
*/
final case class Using[R <: AutoCloseable](open: () => R) extends AnyVal with Keyword[Using[R], R]
object Using {
implicit def implicitUsing[R <: AutoCloseable](r: => R): Using[R] = Using[R](r _)
trait ScopeExitHandler extends AutoCloseable
/** Returns a [[Using]] keyword to execute a [[ScopeExitHandler]] when exiting the nearest enclosing scope
* that is annotated as [[Dsl.reset @reset]],
* (or the nearest enclosing function if [[compilerplugins.ResetEverywhere]] is enabled).
*
* @note This method is similar to [[apply]],
* except the parameter type is changed from a generic `R` to the SAM type [[ScopeExitHandler]],
* which allows for function literal expressions
* in Scala 2.12+ or Scala 2.11 with `-Xexperimental` compiler option.
*
* @example The following function will perform `n *= 2` after `n += 20`:
*
* {{{
* import scala.concurrent.Future
* import com.thoughtworks.dsl.keywords.Using.scopeExit
* var n = 1
* def multiplicationAfterAddition = Future {
* !scopeExit { () =>
* n *= 2
* }
* n += 20
* }
* }}}
*
* Therefore, the final value of `n` should be `(1 + 20) * 2 = 42`.
*
* {{{
* multiplicationAfterAddition.map { _ =>
* n should be(42)
* }
* }}}
*
*/
def scopeExit(r: => ScopeExitHandler) = new Using(r _)
def apply[R <: AutoCloseable](r: => R)(
implicit dummyImplicit: DummyImplicit = DummyImplicit.dummyImplicit): Using[R] = new Using(r _)
implicit def throwableContinuationUsingDsl[Domain, Value, R <: AutoCloseable](
implicit catchDsl: DslCatch[Domain, Domain, Value],
shiftDsl: Dsl[Shift[Domain, Value], Domain, Value]
): Dsl[Using[R], Domain !! Value, R] = { (keyword: Using[R], handler: R => Domain !! Value) =>
_ {
val r = keyword.open()
try {
!Shift(handler(r))
} finally {
r.close()
}
}
}
@deprecated("Use Dsl[Catch[...], ...] as implicit parameters instead of CatchDsl[...]", "Dsl.scala 1.2.0")
private[Using] def throwableContinuationUsingDsl[Domain, Value, R <: AutoCloseable](
implicit catchDsl: CatchDsl[Domain, Domain, Value],
shiftDsl: Dsl[Shift[Domain, Value], Domain, Value]
): Dsl[Using[R], Domain !! Value, R] = {
throwableContinuationUsingDsl(catchDsl: DslCatch[Domain, Domain, Value],
shiftDsl: Dsl[Shift[Domain, Value], Domain, Value])
}
implicit def scalaFutureUsingDsl[R <: AutoCloseable, A](implicit executionContext: ExecutionContext)
: Dsl[Using[R], Future[A], R] = { (keyword: Using[R], handler: R => Future[A]) =>
Future(keyword.open()).flatMap { r: R =>
def onFailure(e: Throwable): Future[Nothing] = {
try {
r.close()
Future.failed(e)
} catch {
case NonFatal(e2) =>
Future.failed(e2)
}
}
def onSuccess(a: A): Future[A] = {
try {
r.close()
Future.successful(a)
} catch {
case NonFatal(e2) =>
Future.failed(e2)
}
}
def returnableBlock(): Future[A] = {
val fa: Future[A] = try {
handler(r)
} catch {
case NonFatal(e) =>
return onFailure(e)
}
fa.recoverWith {
case NonFatal(e) =>
onFailure(e)
}
.flatMap(onSuccess)
}
returnableBlock()
}
}
}