From a390eb2666bd9274b985e8726d038c6ad6b4859d Mon Sep 17 00:00:00 2001 From: yunion Date: Thu, 23 May 2024 13:17:17 -0400 Subject: [PATCH 1/3] add scala3 and scala 2 redis uri interpolators --- build.sbt | 1 + .../dev/profunktor/redis4cats/macros.scala | 36 +++++++++++++++++++ .../dev/profunktor/redis4cats/syntax.scala | 30 ++++++++++++++++ .../dev.profunktor.redis4cats/syntax.scala | 21 +++++++++++ .../redis4cats/connection/RedisURI.scala | 15 +++++++- project/Dependencies.scala | 4 +++ 6 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 modules/core/src/main/scala-2/dev/profunktor/redis4cats/macros.scala create mode 100644 modules/core/src/main/scala-2/dev/profunktor/redis4cats/syntax.scala create mode 100644 modules/core/src/main/scala-3/dev.profunktor.redis4cats/syntax.scala diff --git a/build.sbt b/build.sbt index 753668af..25c48a13 100644 --- a/build.sbt +++ b/build.sbt @@ -107,6 +107,7 @@ lazy val `redis4cats-root` = project lazy val `redis4cats-core` = project .in(file("modules/core")) .settings(commonSettings: _*) + .settings(libraryDependencies ++= Seq(Libraries.literally,Libraries.reflect(scalaVersion.value))) .settings(isMimaEnabled := true) .settings(Test / parallelExecution := false) .enablePlugins(AutomateHeaderPlugin) diff --git a/modules/core/src/main/scala-2/dev/profunktor/redis4cats/macros.scala b/modules/core/src/main/scala-2/dev/profunktor/redis4cats/macros.scala new file mode 100644 index 00000000..0eda3184 --- /dev/null +++ b/modules/core/src/main/scala-2/dev/profunktor/redis4cats/macros.scala @@ -0,0 +1,36 @@ +/* + * Copyright 2018-2021 ProfunKtor + * + * 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 dev.profunktor.redis4cats + +import dev.profunktor.redis4cats.connection.RedisURI +import org.typelevel.literally.Literally + +object macros { + + object redis extends Literally[RedisURI] { + + override def validate(c: Context)(s: String): Either[String, c.Expr[RedisURI]] = { + import c.universe._ + RedisURI.fromString(s) match { + case Left(e) => Left(e.getMessage) + case Right(_) => Right(c.Expr(q"dev.profunktor.redis4cats.connection.RedisURI.unsafeFromString($s)")) + } + } + def make(c: Context)(args: c.Expr[Any]*): c.Expr[RedisURI] = apply(c)(args: _*) + } + +} diff --git a/modules/core/src/main/scala-2/dev/profunktor/redis4cats/syntax.scala b/modules/core/src/main/scala-2/dev/profunktor/redis4cats/syntax.scala new file mode 100644 index 00000000..882655fd --- /dev/null +++ b/modules/core/src/main/scala-2/dev/profunktor/redis4cats/syntax.scala @@ -0,0 +1,30 @@ +/* + * Copyright 2018-2021 ProfunKtor + * + * 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 dev.profunktor.redis4cats + +import dev.profunktor.redis4cats.connection.RedisURI + +object syntax extends syntax { + class RedisURIOps(val sc: StringContext) extends AnyVal { + def redis(args: Any*): RedisURI = macro macros.redis.make + } +} + +trait syntax { + implicit def toRedisURIOps(sc: StringContext): syntax.RedisURIOps = + new syntax.RedisURIOps(sc) +} diff --git a/modules/core/src/main/scala-3/dev.profunktor.redis4cats/syntax.scala b/modules/core/src/main/scala-3/dev.profunktor.redis4cats/syntax.scala new file mode 100644 index 00000000..d7517aca --- /dev/null +++ b/modules/core/src/main/scala-3/dev.profunktor.redis4cats/syntax.scala @@ -0,0 +1,21 @@ +package dev.profunktor.redis4cats + +import dev.profunktor.redis4cats.connection.RedisURI +import org.typelevel.literally.Literally +import scala.language.`3.0` + +trait LiteralsSyntax { + extension (inline ctx: StringContext) { + inline def redis(inline args: Any*): RedisURI = ${ LiteralsSyntax.RedisLiteral('ctx, 'args) } +} + + object LiteralsSyntax extends LiteralsSyntax { + object RedisLiteral extends Literally[Uri] { + def validate(s: String)(using Quotes):Either[String,RedisURI] = { + RedisURI.fromString(s) match { + case Left(e) => Left(e.getMessage) + case Right(_) => Right('{ RedisURI.unsafeFromString(${ Expr(s) }) }) + } + } + } + } diff --git a/modules/core/src/main/scala/dev/profunktor/redis4cats/connection/RedisURI.scala b/modules/core/src/main/scala/dev/profunktor/redis4cats/connection/RedisURI.scala index 53bfae16..b70fb296 100644 --- a/modules/core/src/main/scala/dev/profunktor/redis4cats/connection/RedisURI.scala +++ b/modules/core/src/main/scala/dev/profunktor/redis4cats/connection/RedisURI.scala @@ -17,13 +17,26 @@ package dev.profunktor.redis4cats.connection import cats.ApplicativeThrow +import cats.implicits.toBifunctorOps import io.lettuce.core.{ RedisURI => JRedisURI } -sealed abstract case class RedisURI private (underlying: JRedisURI) +import scala.util.Try +import scala.util.control.NoStackTrace + +sealed abstract class RedisURI private (val underlying: JRedisURI) object RedisURI { def make[F[_]: ApplicativeThrow](uri: => String): F[RedisURI] = ApplicativeThrow[F].catchNonFatal(new RedisURI(JRedisURI.create(uri)) {}) def fromUnderlying(j: JRedisURI): RedisURI = new RedisURI(j) {} + + def fromString(uri: String): Either[InvalidRedisURI, RedisURI] = + Try(JRedisURI.create(uri)).toEither.bimap(InvalidRedisURI(uri, _), new RedisURI(_) {}) + + def unsafeFromString(uri: String): RedisURI = new RedisURI(JRedisURI.create(uri)) {} +} + +final case class InvalidRedisURI(uri: String, throwable: Throwable) extends NoStackTrace { + override def getMessage: String = Option(throwable.getMessage).getOrElse(s"Invalid Redis URI: $uri") } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 69867dec..933534f1 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -30,6 +30,10 @@ object Dependencies { val redisClient = "io.lettuce" % "lettuce-core" % V.lettuce + val literally = "org.typelevel" %% "literally" % "1.2.0" + + def reflect(version:String): ModuleID = "org.scala-lang" % "scala-reflect" % version + // Examples libraries val catsEffect = "org.typelevel" %% "cats-effect" % V.catsEffect val circeCore = "io.circe" %% "circe-core" % V.circe From b22861ea8f4f0489641a5cef622344ff43ff9515 Mon Sep 17 00:00:00 2001 From: yunion Date: Thu, 23 May 2024 14:30:20 -0400 Subject: [PATCH 2/3] - configured scalafmt to bypass scala-3 as macros are not supported in current version and upgrade breaks many things - addressed feedback - provided documentation and proof of usage with mdoc --- .scalafmt.conf | 6 ++ build.sbt | 6 +- .../RedisURIOps.scala} | 19 +++-- .../redis4cats/{ => syntax}/macros.scala | 5 +- .../dev.profunktor.redis4cats/syntax.scala | 21 ----- .../redis4cats}/TypeInqualityCompat.scala | 0 .../dev/profunktor/redis4cats/syntax.scala | 35 ++++++++ project/Dependencies.scala | 2 +- project/MimaVersionPlugin.scala | 84 +++++++++---------- site/docs/client.md | 22 +++++ 10 files changed, 124 insertions(+), 76 deletions(-) rename modules/core/src/main/scala-2/dev/profunktor/redis4cats/{syntax.scala => syntax/RedisURIOps.scala} (64%) rename modules/core/src/main/scala-2/dev/profunktor/redis4cats/{ => syntax}/macros.scala (92%) delete mode 100644 modules/core/src/main/scala-3/dev.profunktor.redis4cats/syntax.scala rename modules/core/src/main/scala-3/{dev.profunktor.redis4cats => dev/profunktor/redis4cats}/TypeInqualityCompat.scala (100%) create mode 100644 modules/core/src/main/scala-3/dev/profunktor/redis4cats/syntax.scala diff --git a/.scalafmt.conf b/.scalafmt.conf index 069ac9a8..c5ab00fe 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -19,3 +19,9 @@ rewriteTokens { "→" = "->" "←" = "<-" } + +project { + excludeFilters = [ + "/scala-3/" + ] +} \ No newline at end of file diff --git a/build.sbt b/build.sbt index 25c48a13..c6145eb3 100644 --- a/build.sbt +++ b/build.sbt @@ -107,7 +107,11 @@ lazy val `redis4cats-root` = project lazy val `redis4cats-core` = project .in(file("modules/core")) .settings(commonSettings: _*) - .settings(libraryDependencies ++= Seq(Libraries.literally,Libraries.reflect(scalaVersion.value))) + .settings(libraryDependencies += Libraries.literally) + .settings( + libraryDependencies ++= + pred(scalaVersion.value.startsWith("3"), t = Seq.empty, f = Seq(Libraries.reflect(scalaVersion.value))) + ) .settings(isMimaEnabled := true) .settings(Test / parallelExecution := false) .enablePlugins(AutomateHeaderPlugin) diff --git a/modules/core/src/main/scala-2/dev/profunktor/redis4cats/syntax.scala b/modules/core/src/main/scala-2/dev/profunktor/redis4cats/syntax/RedisURIOps.scala similarity index 64% rename from modules/core/src/main/scala-2/dev/profunktor/redis4cats/syntax.scala rename to modules/core/src/main/scala-2/dev/profunktor/redis4cats/syntax/RedisURIOps.scala index 882655fd..30afc935 100644 --- a/modules/core/src/main/scala-2/dev/profunktor/redis4cats/syntax.scala +++ b/modules/core/src/main/scala-2/dev/profunktor/redis4cats/syntax/RedisURIOps.scala @@ -14,17 +14,20 @@ * limitations under the License. */ -package dev.profunktor.redis4cats +package dev.profunktor.redis4cats.syntax import dev.profunktor.redis4cats.connection.RedisURI +import dev.profunktor.redis4cats.syntax.macros -object syntax extends syntax { - class RedisURIOps(val sc: StringContext) extends AnyVal { - def redis(args: Any*): RedisURI = macro macros.redis.make - } +class RedisURIOps(val sc: StringContext) extends AnyVal { + def redis(args: Any*): RedisURI = macro macros.RedisLiteral.make } -trait syntax { - implicit def toRedisURIOps(sc: StringContext): syntax.RedisURIOps = - new syntax.RedisURIOps(sc) +trait RedisSyntax { + implicit def toRedisURIOps(sc: StringContext): RedisURIOps = + new RedisURIOps(sc) } + +object literals extends RedisSyntax + + diff --git a/modules/core/src/main/scala-2/dev/profunktor/redis4cats/macros.scala b/modules/core/src/main/scala-2/dev/profunktor/redis4cats/syntax/macros.scala similarity index 92% rename from modules/core/src/main/scala-2/dev/profunktor/redis4cats/macros.scala rename to modules/core/src/main/scala-2/dev/profunktor/redis4cats/syntax/macros.scala index 0eda3184..4c197c11 100644 --- a/modules/core/src/main/scala-2/dev/profunktor/redis4cats/macros.scala +++ b/modules/core/src/main/scala-2/dev/profunktor/redis4cats/syntax/macros.scala @@ -14,14 +14,14 @@ * limitations under the License. */ -package dev.profunktor.redis4cats +package dev.profunktor.redis4cats.syntax import dev.profunktor.redis4cats.connection.RedisURI import org.typelevel.literally.Literally object macros { - object redis extends Literally[RedisURI] { + object RedisLiteral extends Literally[RedisURI] { override def validate(c: Context)(s: String): Either[String, c.Expr[RedisURI]] = { import c.universe._ @@ -30,6 +30,7 @@ object macros { case Right(_) => Right(c.Expr(q"dev.profunktor.redis4cats.connection.RedisURI.unsafeFromString($s)")) } } + def make(c: Context)(args: c.Expr[Any]*): c.Expr[RedisURI] = apply(c)(args: _*) } diff --git a/modules/core/src/main/scala-3/dev.profunktor.redis4cats/syntax.scala b/modules/core/src/main/scala-3/dev.profunktor.redis4cats/syntax.scala deleted file mode 100644 index d7517aca..00000000 --- a/modules/core/src/main/scala-3/dev.profunktor.redis4cats/syntax.scala +++ /dev/null @@ -1,21 +0,0 @@ -package dev.profunktor.redis4cats - -import dev.profunktor.redis4cats.connection.RedisURI -import org.typelevel.literally.Literally -import scala.language.`3.0` - -trait LiteralsSyntax { - extension (inline ctx: StringContext) { - inline def redis(inline args: Any*): RedisURI = ${ LiteralsSyntax.RedisLiteral('ctx, 'args) } -} - - object LiteralsSyntax extends LiteralsSyntax { - object RedisLiteral extends Literally[Uri] { - def validate(s: String)(using Quotes):Either[String,RedisURI] = { - RedisURI.fromString(s) match { - case Left(e) => Left(e.getMessage) - case Right(_) => Right('{ RedisURI.unsafeFromString(${ Expr(s) }) }) - } - } - } - } diff --git a/modules/core/src/main/scala-3/dev.profunktor.redis4cats/TypeInqualityCompat.scala b/modules/core/src/main/scala-3/dev/profunktor/redis4cats/TypeInqualityCompat.scala similarity index 100% rename from modules/core/src/main/scala-3/dev.profunktor.redis4cats/TypeInqualityCompat.scala rename to modules/core/src/main/scala-3/dev/profunktor/redis4cats/TypeInqualityCompat.scala diff --git a/modules/core/src/main/scala-3/dev/profunktor/redis4cats/syntax.scala b/modules/core/src/main/scala-3/dev/profunktor/redis4cats/syntax.scala new file mode 100644 index 00000000..9feeed39 --- /dev/null +++ b/modules/core/src/main/scala-3/dev/profunktor/redis4cats/syntax.scala @@ -0,0 +1,35 @@ +/* + * Copyright 2018-2021 ProfunKtor + * + * 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 dev.profunktor.redis4cats.syntax + +import dev.profunktor.redis4cats.connection.RedisURI +import org.typelevel.literally.Literally +import scala.language.`3.0` + +object literals { + extension (inline ctx: StringContext){ + inline def redis(inline args: Any*):RedisURI = ${RedisLiteral('ctx, 'args)} + } + + object RedisLiteral extends Literally[RedisURI]{ + def validate(s: String)(using Quotes) = + RedisURI.fromString(s) match { + case Left(e) => Left(e.getMessage) + case Right(_) => Right('{RedisURI.unsafeFromString(${ Expr(s) })}) + } + } +} \ No newline at end of file diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 933534f1..274c38a7 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -32,7 +32,7 @@ object Dependencies { val literally = "org.typelevel" %% "literally" % "1.2.0" - def reflect(version:String): ModuleID = "org.scala-lang" % "scala-reflect" % version + def reflect(version: String): ModuleID = "org.scala-lang" % "scala-reflect" % version // Examples libraries val catsEffect = "org.typelevel" %% "cats-effect" % V.catsEffect diff --git a/project/MimaVersionPlugin.scala b/project/MimaVersionPlugin.scala index a2246135..30d88a90 100644 --- a/project/MimaVersionPlugin.scala +++ b/project/MimaVersionPlugin.scala @@ -19,7 +19,7 @@ object MimaVersionPlugin extends AutoPlugin { override def trigger = allRequirements object autoImport { - val ReleaseTag = """^v((?:\d+\.){2}\d+(?:-.*)?)$""".r + val ReleaseTag = """^v((?:\d+\.){2}\d+(?:-.*)?)$""".r lazy val mimaBaseVersion = git.baseVersion lazy val mimaReportBinaryIssuesIfRelevant = taskKey[Unit]( "A wrapper around the mima task which ensures publishArtifact is set to true" @@ -45,44 +45,45 @@ object MimaVersionPlugin extends AutoPlugin { override def buildSettings: Seq[Setting[_]] = GitPlugin.autoImport.versionWithGit ++ Seq( - git.gitTagToVersionNumber := { - case ReleaseTag(version) => Some(version) - case _ => None - }, - git.formattedShaVersion := { - val suffix = git.makeUncommittedSignifierSuffix( - git.gitUncommittedChanges.value, - git.uncommittedSignifier.value + git.gitTagToVersionNumber := { + case ReleaseTag(version) => Some(version) + case _ => None + }, + git.formattedShaVersion := { + val suffix = git.makeUncommittedSignifierSuffix( + git.gitUncommittedChanges.value, + git.uncommittedSignifier.value + ) + + val description = Try("git describe --tags --match v*".!!.trim).toOption + val optDistance = description collect { + case Description(distance) => + distance + "-" + } + + val distance = optDistance.getOrElse("") + + git.gitHeadCommit.value map { _.substring(0, 7) } map { sha => + autoImport.mimaBaseVersion.value + "-" + distance + sha + suffix + } + }, + git.gitUncommittedChanges := Try("git status -s".!!.trim.length > 0) + .getOrElse(true), + git.gitHeadCommit := Try("git rev-parse HEAD".!!.trim).toOption, + git.gitCurrentTags := Try( + "git tag --contains HEAD".!!.trim.split("\\s+").toList.filter(_ != "") + ).toOption.toList.flatten ) - val description = Try("git describe --tags --match v*".!!.trim).toOption - val optDistance = description collect { case Description(distance) => - distance + "-" - } - - val distance = optDistance.getOrElse("") - - git.gitHeadCommit.value map { _.substring(0, 7) } map { sha => - autoImport.mimaBaseVersion.value + "-" + distance + sha + suffix - } - }, - git.gitUncommittedChanges := Try("git status -s".!!.trim.length > 0) - .getOrElse(true), - git.gitHeadCommit := Try("git rev-parse HEAD".!!.trim).toOption, - git.gitCurrentTags := Try( - "git tag --contains HEAD".!!.trim.split("\\s+").toList.filter(_ != "") - ).toOption.toList.flatten - ) - override def projectSettings: Seq[Setting[_]] = Seq( isMimaEnabled := false, mimaReportBinaryIssuesIfRelevant := filterTaskWhereRelevant( - mimaReportBinaryIssues - ).value, - mimaPreviousArtifacts := { + mimaReportBinaryIssues + ).value, + mimaPreviousArtifacts := { val current = version.value - val org = organization.value - val n = moduleName.value + val org = organization.value + val n = moduleName.value val FullTag = """^(\d+)\.(\d+)\.(\d+).*""" r val TagBase = """^(\d+)\.(\d+).*""" r @@ -100,8 +101,6 @@ object MimaVersionPlugin extends AutoPlugin { val tags = scala.util .Try("git tag --list".!!.split("\n").map(_.trim)) .getOrElse(new Array[String](0)) - println(tags.mkString("\n")) - // in semver, we allow breakage in minor releases if major is 0, otherwise not val Pattern = if (isPre) @@ -109,9 +108,10 @@ object MimaVersionPlugin extends AutoPlugin { else s"^v($major\\.\\d+\\.\\d+)$$".r - val versions = tags collect { case Pattern(version) => - version - } + val versions = tags collect { + case Pattern(version) => + version + } def lessThanPatch(patch: String): String => Boolean = { tagVersion => val FullTag(_, _, tagPatch) = tagVersion @@ -123,17 +123,15 @@ object MimaVersionPlugin extends AutoPlugin { .filterNot { val patchPredicate = maybePatch - // if mimaBaseVersion has a patch version, exclude this version if the patch is smaller + // if mimaBaseVersion has a patch version, exclude this version if the patch is smaller .map(lessThanPatch(_)) // else keep the version - .getOrElse { (_: String) => false } + .getOrElse((_: String) => false) v => patchPredicate(v) } notCurrent - .map(v => - projectID.value.withRevision(v).withExplicitArtifacts(Vector.empty) - ) + .map(v => projectID.value.withRevision(v).withExplicitArtifacts(Vector.empty)) .toSet } } diff --git a/site/docs/client.md b/site/docs/client.md index 4c083a01..65bc2f99 100644 --- a/site/docs/client.md +++ b/site/docs/client.md @@ -85,6 +85,28 @@ val configuredApi: Resource[IO, StringCommands[IO, String, String]] = } yield redis ``` +A RedisURI can also be created using the `redis` string interpolator: + +```scala mdoc:silent +import dev.profunktor.redis4cats.syntax.literals._ +val uri = redis"redis://localhost" + +val secure = redis"rediss://localhost" + +val withPassword = redis"redis://:password@localhost" + +val withDatabase = redis"redis://localhost/1" + +val sentinel = redis"redis-sentinel://localhost:26379,localhost:26380?sentinelMasterId=m" + +val `redis+ssl` = redis"redis+ssl://localhost" + +val `redis+tls` = redis"redis+tls://localhost" + +val `redis-socket` = redis"redis-socket:///tmp/redis.sock" + +``` + ## Single node connection For those who only need a simple API access to Redis commands, there are a few ways to acquire a connection: From e696044c11150c63c503fcd58e522a068ad6c262 Mon Sep 17 00:00:00 2001 From: yunion Date: Fri, 24 May 2024 15:55:35 -0400 Subject: [PATCH 3/3] fix --- .../scala-2/dev/profunktor/redis4cats/syntax/RedisURIOps.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/core/src/main/scala-2/dev/profunktor/redis4cats/syntax/RedisURIOps.scala b/modules/core/src/main/scala-2/dev/profunktor/redis4cats/syntax/RedisURIOps.scala index 30afc935..ff035086 100644 --- a/modules/core/src/main/scala-2/dev/profunktor/redis4cats/syntax/RedisURIOps.scala +++ b/modules/core/src/main/scala-2/dev/profunktor/redis4cats/syntax/RedisURIOps.scala @@ -17,7 +17,6 @@ package dev.profunktor.redis4cats.syntax import dev.profunktor.redis4cats.connection.RedisURI -import dev.profunktor.redis4cats.syntax.macros class RedisURIOps(val sc: StringContext) extends AnyVal { def redis(args: Any*): RedisURI = macro macros.RedisLiteral.make