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

outwatch.Render.scala Maven / Gradle / Ivy

There is a newer version: 1.1.0
Show newest version
package outwatch

import cats.Show
import colibri._
import colibri.effect._
import cats.data.{Chain, NonEmptyChain, NonEmptyList, NonEmptySeq, NonEmptyVector}
import cats.effect.{IO, Resource, Sync, SyncIO}

import scala.scalajs.js
import scala.concurrent.Future
import scala.util.Success

trait Render[-T] {
  def render(value: T): VMod
}

trait RenderLowPrio1 {
  import RenderOps._

  @inline implicit def ShowRenderAs[T: Show]: Render[T] = new ShowRenderAsClass[T]

  @inline private class ShowRenderAsClass[T: Show] extends Render[T] {
    @inline def render(value: T): VMod = StringVNode(Show[T].show(value))
  }

  @inline implicit def UndefinedModifierAs[T: Render]: Render[js.UndefOr[T]] = new UndefinedRenderAsClass[T]
  @inline private class UndefinedRenderAsClass[T: Render] extends Render[js.UndefOr[T]] {
    @inline def render(value: js.UndefOr[T]) = undefinedToModifierRender(value)
  }

  implicit object UndefinedModifier extends Render[js.UndefOr[VMod]] {
    @inline def render(value: js.UndefOr[VMod]): VMod = value.getOrElse(VMod.empty)
  }
}

trait RenderLowPrio0 extends RenderLowPrio1 {
  import RenderOps._

  @inline implicit def EffectRender[F[_]: RunEffect]: Render[F[VMod]] = new EffectRenderClass[F]
  @inline private class EffectRenderClass[F[_]: RunEffect] extends Render[F[VMod]] {
    @inline def render(effect: F[VMod]) = effectToModifier(effect)
  }

  @inline implicit def EffectRenderAs[F[_]: RunEffect, T: Render]: Render[F[T]] = new EffectRenderAsClass[F, T]
  @inline private class EffectRenderAsClass[F[_]: RunEffect, T: Render] extends Render[F[T]] {
    @inline def render(effect: F[T]) = effectToModifierRender(effect)
  }
}

trait RenderLowPrio extends RenderLowPrio0 {
  import RenderOps._

  @inline implicit def JsArrayModifierAs[T: Render]: Render[js.Array[T]] = new JsArrayRenderAsClass[T]
  @inline private class JsArrayRenderAsClass[T: Render] extends Render[js.Array[T]] {
    @inline def render(value: js.Array[T]): VMod = iterableToModifierRender(value)
  }

  @inline implicit def ArrayModifierAs[T: Render]: Render[Array[T]] = new ArrayRenderAsClass[T]
  @inline private class ArrayRenderAsClass[T: Render] extends Render[Array[T]] {
    @inline def render(value: Array[T]): VMod = iterableToModifierRender(value)
  }

  @inline implicit def SeqModifierAs[T: Render]: Render[Seq[T]] = new SeqRenderAsClass[T]
  @inline private class SeqRenderAsClass[T: Render] extends Render[Seq[T]] {
    @inline def render(value: Seq[T]): VMod = iterableToModifierRender(value)
  }

  @inline implicit def ChainModifierAs[T: Render]: Render[Chain[T]] = new ChainRenderAsClass[T]
  @inline private class ChainRenderAsClass[T: Render] extends Render[Chain[T]] {
    @inline def render(value: Chain[T]) = iterableToModifierRender(value.toList)
  }

  @inline implicit def NonEmptyListModifierAs[T: Render]: Render[NonEmptyList[T]] = new NonEmptyListRenderAsClass[T]
  @inline private class NonEmptyListRenderAsClass[T: Render] extends Render[NonEmptyList[T]] {
    @inline def render(value: NonEmptyList[T]) = iterableToModifierRender(value.toList)
  }

  @inline implicit def NonEmptyVectorModifierAs[T: Render]: Render[NonEmptyVector[T]] =
    new NonEmptyVectorRenderAsClass[T]
  @inline private class NonEmptyVectorRenderAsClass[T: Render] extends Render[NonEmptyVector[T]] {
    @inline def render(value: NonEmptyVector[T]) = iterableToModifierRender(value.toVector)
  }

  @inline implicit def NonEmptySeqModifierAs[T: Render]: Render[NonEmptySeq[T]] = new NonEmptySeqRenderAsClass[T]
  @inline private class NonEmptySeqRenderAsClass[T: Render] extends Render[NonEmptySeq[T]] {
    @inline def render(value: NonEmptySeq[T]) = iterableToModifierRender(value.toSeq)
  }

  @inline implicit def NonEmptyChainModifierAs[T: Render]: Render[NonEmptyChain[T]] = new NonEmptyChainRenderAsClass[T]
  @inline private class NonEmptyChainRenderAsClass[T: Render] extends Render[NonEmptyChain[T]] {
    @inline def render(value: NonEmptyChain[T]) = iterableToModifierRender(value.toChain.toList)
  }

  @inline implicit def OptionModifierAs[T: Render]: Render[Option[T]] = new OptionRenderAsClass[T]
  @inline private class OptionRenderAsClass[T: Render] extends Render[Option[T]] {
    @inline def render(value: Option[T]): VMod = optionToModifierRender(value)
  }

  @inline implicit def SyncEffectRenderAs[F[_]: RunSyncEffect, T: Render]: Render[F[T]] =
    new SyncEffectRenderAsClass[F, T]
  @inline private class SyncEffectRenderAsClass[F[_]: RunSyncEffect, T: Render] extends Render[F[T]] {
    @inline def render(effect: F[T]): VMod = syncToModifierRender(effect)
  }

  @inline implicit def ResourceRender[F[_]: RunEffect: Sync]: Render[Resource[F, VMod]] =
    new ResourceRenderClass[F]
  @inline private class ResourceRenderClass[F[_]: RunEffect: Sync] extends Render[Resource[F, VMod]] {
    @inline def render(resource: Resource[F, VMod]) = resourceToModifier(resource)
  }

  @inline implicit def ResourceRenderAs[F[_]: RunEffect: Sync, T: Render]: Render[Resource[F, T]] =
    new ResourceRenderAsClass[F, T]
  @inline private class ResourceRenderAsClass[F[_]: RunEffect: Sync, T: Render] extends Render[Resource[F, T]] {
    @inline def render(resource: Resource[F, T]) = resourceToModifierRender(resource)
  }

  @inline implicit def FutureRenderAs[T: Render]: Render[Future[T]] = new FutureRenderAsClass[T]
  @inline private class FutureRenderAsClass[T: Render] extends Render[Future[T]] {
    @inline def render(future: Future[T]) = futureToModifierRender(future)
  }

  @inline implicit def SourceRenderAs[F[_]: Source, T: Render]: Render[F[T]] = new SourceRenderAsClass[F, T]
  @inline private class SourceRenderAsClass[F[_]: Source, T: Render] extends Render[F[T]] {
    @inline def render(source: F[T]): VMod = sourceToModifierRender(source)
  }
}

object Render extends RenderLowPrio {
  @inline def apply[T](implicit render: Render[T]): Render[T] = render

  import RenderOps._

  implicit object JsArrayModifier extends Render[js.Array[VMod]] {
    @inline def render(value: js.Array[VMod]): VMod = CompositeModifier(value)
  }

  implicit object ArrayModifier extends Render[Array[VMod]] {
    @inline def render(value: Array[VMod]): VMod = CompositeModifier(value)
  }

  implicit object SeqModifier extends Render[Seq[VMod]] {
    @inline def render(value: Seq[VMod]): VMod = CompositeModifier(value)
  }

  implicit object ChainModifier extends Render[Chain[VMod]] {
    @inline def render(value: Chain[VMod]): VMod = CompositeModifier(value.toList)
  }

  implicit object NonEmptyListModifier extends Render[NonEmptyList[VMod]] {
    @inline def render(value: NonEmptyList[VMod]): VMod = CompositeModifier(value.toList)
  }

  implicit object NonEmptyVectorModifier extends Render[NonEmptyVector[VMod]] {
    @inline def render(value: NonEmptyVector[VMod]): VMod = CompositeModifier(value.toVector)
  }

  implicit object NonEmptySeqModifier extends Render[NonEmptySeq[VMod]] {
    @inline def render(value: NonEmptySeq[VMod]): VMod = CompositeModifier(value.toSeq)
  }

  implicit object NonEmptyChainModifier extends Render[NonEmptyChain[VMod]] {
    @inline def render(value: NonEmptyChain[VMod]): VMod = CompositeModifier(value.toChain.toList)
  }

  implicit object OptionModifier extends Render[Option[VMod]] {
    @inline def render(value: Option[VMod]): VMod = value.getOrElse(VMod.empty)
  }

  implicit object VModRender extends Render[VMod] {
    @inline def render(value: VMod): VMod = value
  }

  implicit object StringRender extends Render[String] {
    @inline def render(value: String): VMod = StringVNode(value)
  }

  implicit object IntRender extends Render[Int] {
    @inline def render(value: Int): VMod = StringVNode(value.toString)
  }

  implicit object DoubleRender extends Render[Double] {
    @inline def render(value: Double): VMod = StringVNode(value.toString)
  }

  implicit object LongRender extends Render[Long] {
    @inline def render(value: Long): VMod = StringVNode(value.toString)
  }

  implicit object BooleanRender extends Render[Boolean] {
    @inline def render(value: Boolean): VMod = StringVNode(value.toString)
  }

  implicit object SyncIORender extends Render[SyncIO[VMod]] {
    @inline def render(future: SyncIO[VMod]) = syncToModifier(future)
  }

  @inline implicit def SyncEffectRender[F[_]: RunSyncEffect]: Render[F[VMod]] = new SyncEffectRenderClass[F]
  @inline private class SyncEffectRenderClass[F[_]: RunSyncEffect] extends Render[F[VMod]] {
    @inline def render(effect: F[VMod]) = syncToModifier(effect)
  }

  implicit object IORender extends Render[IO[VMod]] {
    @inline def render(future: IO[VMod]) = effectToModifier(future)
  }

  implicit object FutureRender extends Render[Future[VMod]] {
    @inline def render(future: Future[VMod]) = futureToModifier(future)
  }

  implicit object ObservableRender extends Render[Observable[VMod]] {
    @inline def render(source: Observable[VMod]) = sourceToModifier(source)
  }

  @inline implicit def SourceRender[F[_]: Source]: Render[F[VMod]] = new SourceRenderClass[F]
  @inline private class SourceRenderClass[F[_]: Source] extends Render[F[VMod]] {
    @inline def render(source: F[VMod]) = sourceToModifier(source)
  }

  @inline implicit def AttrBuilderRender: Render[AttrBuilder[Boolean, VMod]] = new AttrBuilderRender
  @inline private final class AttrBuilderRender extends Render[AttrBuilder[Boolean, VMod]] {
    @inline def render(builder: AttrBuilder[Boolean, VMod]) = builder := true
  }

  @inline implicit def EmitterBuilderRender: Render[EmitterBuilder[VMod, VMod]] = new EmitterBuilderRender
  @inline private final class EmitterBuilderRender extends Render[EmitterBuilder[VMod, VMod]] {
    @inline def render(builder: EmitterBuilder[VMod, VMod]) = builder.render
  }

  @inline implicit def ChildCommandSourceRender[F[_]: Source]: Render[F[ChildCommand]] = new ChildCommandRenderClass[F]
  @inline private class ChildCommandRenderClass[F[_]: Source] extends Render[F[ChildCommand]] {
    @inline def render(source: F[ChildCommand]): VMod = childCommandToModifier(source)
  }

  @inline implicit def ChildCommandSeqSourceRender[F[_]: Source]: Render[F[Seq[ChildCommand]]] =
    new ChildCommandSeqRenderClass[F]
  @inline private class ChildCommandSeqRenderClass[F[_]: Source] extends Render[F[Seq[ChildCommand]]] {
    @inline def render(source: F[Seq[ChildCommand]]): VMod = childCommandSeqToModifier(source)
  }
}

private object RenderOps {
  @noinline def iterableToModifierRender[T: Render](value: Iterable[T]): VMod = CompositeModifier(
    value.map(VMod(_)),
  )
  @noinline def optionToModifierRender[T: Render](value: Option[T]): VMod =
    value.fold(VMod.empty)(VMod(_))
  @noinline def undefinedToModifierRender[T: Render](value: js.UndefOr[T]): VMod =
    value.fold(VMod.empty)(VMod(_))
  @noinline def syncToModifierRender[F[_]: RunSyncEffect, T: Render](effect: F[T]): VMod =
    VMod.evalEither(RunSyncEffect[F].unsafeRun(effect))
  @noinline def syncToModifier[F[_]: RunSyncEffect](effect: F[VMod]): VMod =
    VMod.evalEither(RunSyncEffect[F].unsafeRun(effect))
  @noinline def effectToModifier[F[_]: RunEffect](effect: F[VMod]): VMod = StreamModifier(
    Observable.fromEffect(effect).unsafeSubscribe(_),
  )
  @noinline def effectToModifierRender[F[_]: RunEffect, T: Render](effect: F[T]): VMod =
    StreamModifier(obs => Observable.fromEffect(effect).unsafeSubscribe(obs.contramap(VMod(_))))
  @noinline def resourceToModifier[F[_]: RunEffect: Sync](resource: Resource[F, VMod]): VMod = StreamModifier(
    Observable.fromResource(resource).unsafeSubscribe(_),
  )
  @noinline def resourceToModifierRender[F[_]: RunEffect: Sync, T: Render](resource: Resource[F, T]): VMod =
    StreamModifier(obs => Observable.fromResource(resource).unsafeSubscribe(obs.contramap(VMod(_))))
  @noinline def sourceToModifier[F[_]: Source](source: F[VMod]): VMod = StreamModifier(
    Source[F].unsafeSubscribe(source),
  )
  @noinline def sourceToModifierRender[F[_]: Source, T: Render](source: F[T]): VMod =
    StreamModifier(sink => Source[F].unsafeSubscribe(source)(sink.contramap(VMod(_))))
  @noinline def childCommandSeqToModifier[F[_]: Source](source: F[Seq[ChildCommand]]): VMod =
    ChildCommandsModifier(Observable.lift(source))
  @noinline def childCommandToModifier[F[_]: Source](source: F[ChildCommand]): VMod = ChildCommandsModifier(
    Observable.lift(source).map(Seq(_)),
  )
  @noinline def futureToModifierRender[T: Render](future: Future[T]): VMod = future.value match {
    case Some(Success(value)) => VMod(value)
    case _                    => StreamModifier(Observable.fromFuture(future).map(VMod(_)).unsafeSubscribe(_))
  }
  @noinline def futureToModifier(future: Future[VMod]): VMod = future.value match {
    case Some(Success(value)) => value
    case _                    => StreamModifier(Observable.fromFuture(future).unsafeSubscribe(_))
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy