Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
83b0c08
Proxy macro for Scala 2ˆˆ
SHSongs Apr 19, 2023
3ca5170
wip
guersam Apr 20, 2023
b204ebd
wip
guersam Apr 20, 2023
65d610e
wip
guersam Apr 21, 2023
f1b0b1c
wip: bypass proxy
guersam Apr 22, 2023
a12081d
Initial Scala 3 version
guersam Apr 22, 2023
7eb0237
Revert build settings
guersam Apr 22, 2023
cd72d66
More tests
guersam Apr 22, 2023
8235b52
wip
SHSongs Apr 22, 2023
8bd66d8
Simplify Scala 3 macro
guersam Apr 23, 2023
fd3166e
Further Scala 3 cleanup
guersam Apr 23, 2023
f432f67
More test case
guersam Apr 23, 2023
150cfc1
Scala 3 cleanup
guersam Apr 23, 2023
88e75e2
Remove unnecessary implicit from inheritance test
guersam Apr 23, 2023
7ac0729
test cleanup
guersam Apr 23, 2023
f6f1925
Simply Trace summoning
guersam Apr 23, 2023
a0c51df
Discard unused pattern variable
guersam Apr 23, 2023
40b1bdf
Fix typo
guersam Apr 23, 2023
31adbb2
Forwards overridden methods
guersam Apr 23, 2023
55a1ead
Supports for val
guersam Apr 23, 2023
c0376b6
wip
SHSongs Apr 23, 2023
5fc00ab
Mension upcoming `ValOrDefDef`
guersam Apr 23, 2023
b78a48a
Forwards package private methods
guersam Apr 23, 2023
ed010dc
wip, see Traversal via Pattern Matching
SHSongs Apr 27, 2023
dc35514
Test service switching for both def and val
guersam Apr 27, 2023
e12523f
wip: Rewrite Scala 2 macros
SHSongs Apr 29, 2023
f261ee0
wip: Rewrite Scala 2 macros
SHSongs Apr 29, 2023
faa65ee
ZIO vals should work
guersam Apr 29, 2023
cf04fcc
Rewrite Scala 2 macros
SHSongs Apr 30, 2023
6ae04f2
proxy abstract class, fails to compile test case
SHSongs Apr 30, 2023
456ce0d
Make Scala 2 debug compatible with Scala 3
guersam Apr 30, 2023
497b7e2
Merge remote-tracking branch 'origin/series/2.x' into 7556-proxy-macro
guersam Apr 30, 2023
1293696
Support for classes in Scala 3
guersam Apr 30, 2023
d4f26e1
fail message
SHSongs Apr 30, 2023
5707d92
always make override method
SHSongs Apr 30, 2023
5999f3c
always make override method, fix test
SHSongs Apr 30, 2023
80b369c
scala 2.12 compatibility
SHSongs Apr 30, 2023
edd9646
rename to ServiceProxy
SHSongs Apr 30, 2023
737b1ce
More descriptive error messages in Scala 3
guersam May 1, 2023
a6d8dd9
Add scaladoc
guersam May 1, 2023
bee5eb5
License
guersam May 1, 2023
7b84f5f
More scaladoc
guersam May 1, 2023
c1e98f5
More descriptive error messages in Scala 2
SHSongs May 1, 2023
4152b29
summon Trace in the right scope
guersam May 1, 2023
e3d9dc8
More descriptive error messages in Scala 2
SHSongs May 1, 2023
2e57603
More descriptive error messages in Scala 2, fmt
SHSongs May 1, 2023
0872dfb
Fix suite title
guersam May 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2017-2023 John A. De Goes and the ZIO Contributors
*
* 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 scala
package annotation

/**
* For Scala 3 compatibility
*/
class experimental extends StaticAnnotation
270 changes: 270 additions & 0 deletions core-tests/shared/src/test/scala/zio/ServiceProxySpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
/*
* Copyright 2017-2023 John A. De Goes and the ZIO Contributors
*
* 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 zio

import zio.test._

@scala.annotation.experimental
object ServiceProxySpec extends ZIOSpecDefault {

val spec = suite("ServiceProxy")(
suite("generates a proxy")(
test("switches underlying service in runtime") {
trait Foo { def bar: UIO[String] }

val service1: Foo = new Foo { def bar = ZIO.succeed("zio1") }
val service2: Foo = new Foo { def bar = ZIO.succeed("zio2") }
for {
ref <- ScopedRef.make(service1)
proxy = ServiceProxy.generate(ref)
res1 <- proxy.bar
_ <- ref.set(ZIO.succeed(service2))
res2 <- proxy.bar
} yield assertTrue(res1 == "zio1" && res2 == "zio2")
},
test("the type out of the current lexical scope") {
object foo {
trait Foo { def bar: UIO[String] }
}
val service: foo.Foo = new foo.Foo { def bar = ZIO.succeed("zio") }
for {
ref <- ScopedRef.make(service)
proxy = ServiceProxy.generate(ref)
res1 <- proxy.bar
} yield assertTrue(res1 == "zio")
},
test("multiple methods") {
trait Foo {
def bar: UIO[String]
def baz: UIO[Int]
}
val service: Foo = new Foo {
override def bar: UIO[String] = ZIO.succeed("zio")
override def baz: UIO[Int] = ZIO.succeed(1)
}
for {
ref <- ScopedRef.make(service)
proxy = ServiceProxy.generate(ref)
res1 <- proxy.bar
res2 <- proxy.baz
} yield assertTrue(res1 == "zio" && res2 == 1)
},
test("trait with type parameter") {
trait Foo[A] { def bar: UIO[A] }
val service: Foo[String] = new Foo[String] { def bar = ZIO.succeed("baz") }
for {
ref <- ScopedRef.make(service)
proxy = ServiceProxy.generate(ref)
res <- proxy.bar
} yield assertTrue(res == "baz")
},
test("trait with higher kinded type parameter") {
trait Foo[F[_]] { def bar: UIO[F[String]] }
val service: Foo[List] = new Foo[List] { def bar = ZIO.succeed("baz" :: Nil) }
for {
ref <- ScopedRef.make(service)
proxy = ServiceProxy.generate(ref)
res <- proxy.bar
} yield assertTrue(res == "baz" :: Nil)
},
test("abstract class") {
abstract class Foo { def bar: UIO[String] }
val service: Foo = new Foo { def bar = ZIO.succeed("zio") }
for {
ref <- ScopedRef.make(service)
proxy = ServiceProxy.generate(ref)
res <- proxy.bar
} yield assertTrue(res == "zio")
},
test("class") {
class Foo { def bar: UIO[String] = ZIO.succeed("zio1") }
val service: Foo = new Foo {
override def bar = ZIO.succeed("zio2")
}
for {
ref <- ScopedRef.make(service)
proxy = ServiceProxy.generate(ref)
res <- proxy.bar
} yield assertTrue(res == "zio2")
}
),
suite("forwards")(
test("methods with params") {
trait Foo { def bar(a: String): UIO[Int] }
val service: Foo = new Foo { def bar(a: String) = ZIO.succeed(a.length) }
for {
ref <- ScopedRef.make(service)
proxy = ServiceProxy.generate(ref)
res <- proxy.bar("zio")
} yield assertTrue(res == 3)
},
test("generic methods") {
trait Foo { def bar[A](a: A): UIO[A] }
val service: Foo = new Foo { def bar[A](a: A) = ZIO.succeed(a) }
for {
ref <- ScopedRef.make(service)
proxy = ServiceProxy.generate(ref)
res <- proxy.bar[String]("zio")
} yield assertTrue(res == "zio")
},
test("curried methods") {
trait Foo { def bar(a: Int)(b: String): UIO[String] }
val service: Foo = new Foo { def bar(a: Int)(b: String) = ZIO.succeed(b * a) }
for {
ref <- ScopedRef.make(service)
proxy = ServiceProxy.generate(ref)
res <- proxy.bar(3)("zio")
} yield assertTrue(res == "zioziozio")
},
test("implicit clauses") {
trait Foo { def bar(a: Int)(implicit b: String): UIO[String] }
val service: Foo = new Foo { def bar(a: Int)(implicit b: String) = ZIO.succeed(b * a) }
implicit val b: String = "zio"
for {
ref <- ScopedRef.make(service)
proxy = ServiceProxy.generate(ref)
res <- proxy.bar(3)
} yield assertTrue(res == "zioziozio")
},
test("inherited abstract methods") {
trait Foo0 { def bar(a: Int): UIO[String] }
trait Foo extends Foo0

val service: Foo = new Foo { def bar(a: Int) = ZIO.succeed("zio" * a) }
for {
ref <- ScopedRef.make(service)
proxy = ServiceProxy.generate(ref)
res <- proxy.bar(3)
} yield assertTrue(res == "zioziozio")
},
test("overridden methods with default implementation") {
trait Foo { def bar: UIO[String] = ZIO.succeed("zio1") }
val service: Foo = new Foo { override def bar = ZIO.succeed("zio2") }
for {
ref <- ScopedRef.make(service)
proxy = ServiceProxy.generate(ref)
res <- proxy.bar
} yield assertTrue(res == "zio2")
},
test("package private methods") {
trait Foo { private[zio] def bar: UIO[String] }
val service: Foo = new Foo { private[zio] def bar: UIO[String] = ZIO.succeed("zio") }
for {
ref <- ScopedRef.make(service)
proxy = ServiceProxy.generate(ref)
res <- proxy.bar
} yield assertTrue(res == "zio")
},
test("ZIO vals") {
trait Foo { val bar: UIO[String] }
val service1: Foo = new Foo { val bar: UIO[String] = ZIO.succeed("zio1") }
val service2: Foo = new Foo { val bar: UIO[String] = ZIO.succeed("zio2") }
for {
ref <- ScopedRef.make(service1)
proxy = ServiceProxy.generate(ref)
res1 <- proxy.bar
_ <- ref.set(ZIO.succeed(service2))
res2 <- proxy.bar
} yield assertTrue(res1 == "zio1" && res2 == "zio2")
},
test("ZIO vals with default implementation") {
trait Foo { val bar: UIO[String] = ZIO.succeed("zio") }
val service: Foo = new Foo { override val bar: UIO[String] = ZIO.succeed("zio1") }
for {
ref <- ScopedRef.make(service)
proxy = ServiceProxy.generate(ref)
res1 <- proxy.bar
} yield assertTrue(res1 == "zio1")
},
test("keeps non-ZIO default implementations") {
trait Foo {
def bar: UIO[String]
def qux: String = "quux"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is correct behavior for things like constants. Could be extra strict and require they be final for bulletproof semantics.

}
val service: Foo = new Foo { def bar = ZIO.succeed("baz") }
for {
ref <- ScopedRef.make(service)
proxy = ServiceProxy.generate(ref)
} yield assertTrue(proxy.qux == "quux")
}
),
suite("fails to compile")(
test("non-ZIO abstract method") {
for {
res <- typeCheck(
"""
trait Foo { def qux: String }
val service: Foo = new Foo { def qux = "quux" }
for {
ref <- ScopedRef.make(service)
} yield ServiceProxy.generate(ref)
"""
)
} yield
if (TestVersion.isScala2)
assertTrue(res.swap.exists(_.contains("non-ZIO")))
else
assertTrue(res.isLeft)
},
test("classes/traits with non-empty primary constructor") {
for {
res <- typeCheck(
"""
class Foo(s: String) { def bar: UIO[String] = ZIO.succeed(s) }
val service: Foo = new Foo("zio")
for {
ref <- ScopedRef.make(service)
proxy = ServiceProxy.generate(ref)
res <- proxy.bar
} yield res
"""
)
} yield
if (TestVersion.isScala2)
assertTrue(res.swap.exists(_.contains("non-empty parameters")))
else
assertTrue(res.isLeft)
},
test("abstract type members") {
for {
res <- typeCheck(
"""
trait Foo {
type T
def bar: UIO[T]
}

val service: Foo = new Foo {
type T = String
def bar: UIO[String] = ZIO.succeed("zio")
}
for {
ref <- ScopedRef.make(service)
proxy = ServiceProxy.generate(ref)
res <- proxy.bar
} yield assertTrue(res == "zio")
"""
)
} yield
if (TestVersion.isScala2)
assertTrue(res.swap.exists(_.contains("Abstract")))
else
assertTrue(res.isLeft)
}
)
)
}
37 changes: 37 additions & 0 deletions core/shared/src/main/scala-2/zio/ServiceProxyVersionSpecific.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2017-2023 John A. De Goes and the ZIO Contributors
*
* 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 zio

import zio.internal.macros.ServiceProxyMacros

trait ServiceProxyVersionSpecific {

/**
* Generates a proxy instance of the specified service.
*
* @tparam A
* The type of the service.
* @param service
* The [[zio.ScopedRef]] containing the service for which a proxy is to be
* generated.
* @return
* A proxy instance of the service that forwards ZIO method calls to the
* underlying service and allows the service to change its behavior at
* runtime.
*/
def generate[A](service: ScopedRef[A]): A = macro ServiceProxyMacros.makeImpl[A]
}
Loading