From 83cbf887ac68f951f9a1e42ec1101a6cbc8a84e9 Mon Sep 17 00:00:00 2001 From: Roman Langolf Date: Sun, 29 Mar 2026 15:16:28 +0700 Subject: [PATCH] add CurlZioBackend implementation for ZIO on Scala Native --- build.sbt | 8 ++++++ docs/backends/native/curl.md | 27 +++++++++++++++++++ docs/backends/zio.md | 5 ++++ .../client4/curl/zio/CurlZioBackend.scala | 19 +++++++++++++ .../client4/curl/zio/CurlZioHttpTest.scala | 16 +++++++++++ 5 files changed, 75 insertions(+) create mode 100644 effects/zio/src/main/scalanative/sttp/client4/curl/zio/CurlZioBackend.scala create mode 100644 effects/zio/src/test/scalanative/sttp/client4/curl/zio/CurlZioHttpTest.scala diff --git a/build.sbt b/build.sbt index e649695d80..53071a6d25 100644 --- a/build.sbt +++ b/build.sbt @@ -559,6 +559,14 @@ lazy val zio = (projectMatrix in file("effects/zio")) scalaVersions = scala2And3, settings = commonJsSettings ++ commonJsBackendSettings ++ browserChromeTestSettings ++ testServerSettings ) + .nativePlatform( + scalaVersions = List(scala3), + settings = commonNativeSettings ++ Seq( + libraryDependencies ++= Seq( + "io.github.cquiroz" %%% "scala-java-time" % "2.6.0" + ) + ) + ) lazy val scalaz = (projectMatrix in file("effects/scalaz")) .settings(commonJvmSettings) diff --git a/docs/backends/native/curl.md b/docs/backends/native/curl.md index 3020019889..774404fdef 100644 --- a/docs/backends/native/curl.md +++ b/docs/backends/native/curl.md @@ -38,3 +38,30 @@ import sttp.client4.curl.CurlBackend println(basicRequest.get(uri"http://httpbin.org/ip").send(backend)) ``` +## ZIO-based +To use in an sbt project, add the following dependency: + +``` +"com.softwaremill.sttp.client4" %%% "zio" % @VERSION@ +``` + +Create the backend instance for example via `scoped()` +which will also ensure that acquired resources (if any) are released once out of `Scope`: + +```scala +//> using platform native +//> using nativeVersion 0.5.10 +//> using scala 3 +//> using dep com.softwaremill.sttp.client4::zio::@VERSION@ + +import sttp.client4.* +import sttp.client4.curl.zio.CurlZioBackend +import zio.* + +object Main extends ZIOAppDefault: + def run = for + backend <- CurlZioBackend.scoped() + res <- basicRequest.get(uri"http://httpbin.org/ip").send(backend) + _ <- Console.printLine(res) + yield () +``` \ No newline at end of file diff --git a/docs/backends/zio.md b/docs/backends/zio.md index 5993fca6a2..e64a7892d8 100644 --- a/docs/backends/zio.md +++ b/docs/backends/zio.md @@ -100,6 +100,11 @@ Please visit [the official documentation](https://armeria.dev/docs/client-factor The ZIO backend is also available for the JS platform, see [the `FetchBackend` documentation](javascript/fetch.md). The `FetchBackend` companion object contains methods to create the backend directly, as a layer or scoped. +## Using Scala Native + +The ZIO backend is also available for the native platform backed by curl, see [the `CurlZioBackend` documentation](native/curl.md). +The `CurlZioBackend` companion object contains methods to create the backend directly, as a layer or scoped. + ## ZIO layers + constructors When using constructors to express service dependencies, ZIO layers can be used to provide the `SttpBackend` instance, instead of creating one by hand. In this scenario, the lifecycle of a `SttpBackend` service is described by `ZLayer`s, which can be created using the `.layer`/`.layerUsingConfig`/... methods on `HttpClientZioBackend` / `ArmeriaZioBackend`. diff --git a/effects/zio/src/main/scalanative/sttp/client4/curl/zio/CurlZioBackend.scala b/effects/zio/src/main/scalanative/sttp/client4/curl/zio/CurlZioBackend.scala new file mode 100644 index 0000000000..c104ab0ac5 --- /dev/null +++ b/effects/zio/src/main/scalanative/sttp/client4/curl/zio/CurlZioBackend.scala @@ -0,0 +1,19 @@ +package sttp.client4.curl.zio + +import _root_.zio._ +import sttp.client4.Backend +import sttp.client4.impl.zio.RIOMonadAsyncError + +class CurlZioBackend private (verbose: Boolean) + extends sttp.client4.curl.AbstractSyncCurlBackend[Task](RIOMonadAsyncError[Any], verbose) + with Backend[Task] + +object CurlZioBackend { + def apply(verbose: Boolean = false): CurlZioBackend = new CurlZioBackend(verbose) + + def layer(verbose: Boolean = false): ZLayer[Any, Throwable, CurlZioBackend] = + ZLayer.scoped(scoped(verbose)) + + def scoped(verbose: Boolean = false): ZIO[Scope, Throwable, CurlZioBackend] = + ZIO.acquireRelease(ZIO.attempt(apply(verbose)))(_.close().ignore) +} diff --git a/effects/zio/src/test/scalanative/sttp/client4/curl/zio/CurlZioHttpTest.scala b/effects/zio/src/test/scalanative/sttp/client4/curl/zio/CurlZioHttpTest.scala new file mode 100644 index 0000000000..7571645cfa --- /dev/null +++ b/effects/zio/src/test/scalanative/sttp/client4/curl/zio/CurlZioHttpTest.scala @@ -0,0 +1,16 @@ +package sttp.client4.curl.zio + +import zio.Task +import sttp.client4.testing.HttpTest +import sttp.client4.Backend +import sttp.client4.impl.zio.ZioTestBase +import sttp.client4.testing.ConvertToFuture + +class CurlZioHttpTest extends HttpTest[Task] with ZioTestBase { + val backend: Backend[Task] = CurlZioBackend() + override implicit val convertToFuture: ConvertToFuture[Task] = convertZioTaskToFuture + + override def supportsHostHeaderOverride = false + override def supportsDeflateWrapperChecking = false + override def supportsCancellation = false +}