Skip to content

Commit

Permalink
Merge pull request #49 from typelevel/bplommer/contravariant
Browse files Browse the repository at this point in the history
contravariant
  • Loading branch information
bplommer authored Jun 22, 2023
2 parents 1755e47 + c921e28 commit 56c3677
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 0 deletions.
12 changes: 12 additions & 0 deletions core/src/main/scala/org.typelevel/catapult/ContextEncoder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,28 @@
package org.typelevel.catapult

import com.launchdarkly.sdk.{LDContext, LDUser}
import cats.Contravariant

/** A typeclass for converting values of type `Ctx` into [[https://javadoc.io/doc/com.launchdarkly/launchdarkly-java-server-sdk/latest/com/launchdarkly/sdk/LDContext.html LDContext]]. An instance must be in scope when
* evaulating flags against a context represented by the `Ctx` type. Instances are provided for [[https://javadoc.io/doc/com.launchdarkly/launchdarkly-java-server-sdk/latest/com/launchdarkly/sdk/LDContext.html LDContext]]
* and [[https://javadoc.io/doc/com.launchdarkly/launchdarkly-java-server-sdk/latest/com/launchdarkly/sdk/LDUser.html LDUser]]; custom instances can be created to allow other types to be used.
*/
trait ContextEncoder[Ctx] {
def encode(ctx: Ctx): LDContext

/** Create a new `ContextEncoder` by applying a function to the input before encoding. */
def contramap[A](f: A => Ctx): ContextEncoder[A] = a => encode(f(a))
}

object ContextEncoder {
def apply[Ctx](implicit ev: ContextEncoder[Ctx]): ContextEncoder[Ctx] = ev
def encode[Ctx](ctx: Ctx)(implicit ev: ContextEncoder[Ctx]): LDContext = ev.encode(ctx)

implicit val catapultContextEncoderContravariant: Contravariant[ContextEncoder] =
new Contravariant[ContextEncoder] {
def contramap[A, B](fa: ContextEncoder[A])(f: B => A): ContextEncoder[B] = fa.contramap(f)
}

implicit val catapultContextEncoderForLdContext: ContextEncoder[LDContext] = identity(_)

implicit val catapultContextEncoderForLdUser: ContextEncoder[LDUser] = LDContext.fromUser(_)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2022 Typelevel
*
* 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 org.typelevel.catapult

import com.launchdarkly.sdk.{ContextKind, LDContext}
import weaver.SimpleIOSuite

object ContextEncoderTests extends SimpleIOSuite {
pureTest("contramap allows changing the input type of ContextEncoder") {
// This is a trivial example - it would be just as simple to write a new ContextEncoder from scatch.
case class Country(name: String)

val enc = ContextEncoder[LDContext].contramap[Country](country =>
LDContext.builder(ContextKind.of("country"), country.name).build()
)

val franceCtx = LDContext.create(ContextKind.of("country"), "France")
val germanyCtx = LDContext.create(ContextKind.of("country"), "Germany")

expect.all(
enc.encode(Country("France")) == franceCtx,
enc.encode(Country("Germany")) == germanyCtx,
)
}
}

0 comments on commit 56c3677

Please sign in to comment.