All Downloads are FREE. Search and download functionalities are using the official Maven repository.

pillars.redis_rediculous.redis.scala Maven / Gradle / Ivy

Go to download

pillars-redis-rediculous is a scala 3 library providing redis services for writing backend applications using rediculous

The newest version!
// Copyright (c) 2024-2024 by Raphaël Lemaitre and Contributors
// This software is licensed under the Eclipse Public License v2.0 (EPL-2.0).
// For more information see LICENSE or https://opensource.org/license/epl-2-0

package pillars.redis_rediculous

import cats.effect.*
import cats.effect.std.Console
import cats.implicits.*
import com.comcast.ip4s.*
import fs2.io.file.Files
import fs2.io.net.*
import io.chrisdavenport.rediculous.*
import io.chrisdavenport.rediculous.RedisConnection.Defaults
import io.chrisdavenport.rediculous.RedisProtocol.Status.*
import io.circe.Codec
import io.circe.Decoder as CirceDecoder
import io.circe.Encoder as CirceEncoder
import io.circe.derivation.Configuration
import io.github.iltotore.iron.*
import io.github.iltotore.iron.circe.given
import io.github.iltotore.iron.constraint.all.*
import org.typelevel.otel4s.trace.Tracer
import pillars.Module
import pillars.Modules
import pillars.ModuleSupport
import pillars.Pillars
import pillars.codec.given
import pillars.probes.*

extension [F[_]](p: Pillars[F])
    def redis: Redis[F] = p.module[Redis[F]](Redis.Key)

final case class Redis[F[_]: MonadCancelThrow](config: RedisConfig, connection: Resource[F, RedisConnection[F]])(using
    c: Concurrent[F]
) extends Module[F]:
    override type ModuleConfig = RedisConfig
    export connection.*

    override def probes: List[Probe[F]] =
        val probe = new Probe[F]:
            override def component: Component = Component(Component.Name("redis"), Component.Type.Datastore)
            override def check: F[Boolean]    = connection.use { client =>
                RedisCommands.ping[io.chrisdavenport.rediculous.Redis[F, *]].run(client) map {
                    case Ok | Pong => true
                    case _         => false
                }
            }
        probe.pure[List]
    end probes
end Redis

object Redis extends ModuleSupport:
    case object Key extends Module.Key:
        override val name: String = "redis-rediculous"
    def apply[F[_]](using p: Pillars[F]): Redis[F] = p.module[Redis[F]](Redis.Key)

    override type M[F[_]] = Redis[F]
    override val key: Module.Key = Redis.Key

    def load[F[_]: Async: Network: Tracer: Console](
        context: ModuleSupport.Context[F],
        modules: Modules[F]
    ): Resource[F, Redis[F]] =
        import context.*
        given Files[F] = Files.forAsync[F]
        for
            _         <- Resource.eval(logger.info("Loading Redis module"))
            config    <- Resource.eval(reader.read[RedisConfig]("redis"))
            connection = Redis(
                           config,
                           RedisConnection.queued[F]
                               .withHost(config.host)
                               .withPort(config.port)
                               .withMaxQueued(config.maxQueue)
                               .withWorkers(config.workers)
                               .withAuth(config.username, config.password)
                               .withTLS
                               .build
                         )
            _         <- Resource.eval(logger.info("Redis module loaded"))
        yield connection
        end for
    end load
end Redis

final case class RedisConfig(
    host: Host = host"localhost",
    port: Port = port"6379",
    maxQueue: Int = Defaults.maxQueued,
    workers: Int = Defaults.workers,
    username: Option[RedisUser],
    password: RedisPassword,
    probe: ProbeConfig
) extends pillars.Config

object RedisConfig:
    given Configuration      = Configuration.default.withKebabCaseMemberNames.withKebabCaseConstructorNames.withDefaults
    given Codec[RedisConfig] = Codec.AsObject.derivedConfigured
end RedisConfig

private type RedisUserConstraint = Not[Blank] DescribedAs "Redis user must not be blank"
opaque type RedisUser <: String  = String :| RedisUserConstraint

object RedisUser extends RefinedTypeOps[String, RedisUserConstraint, RedisUser]

private type RedisPasswordConstraint = Not[Blank] DescribedAs "Redis password must not be blank"
opaque type RedisPassword <: String  = String :| RedisPasswordConstraint

object RedisPassword extends RefinedTypeOps[String, RedisPasswordConstraint, RedisPassword]




© 2015 - 2025 Weber Informatics LLC | Privacy Policy