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

io.frontroute.Directive.scala Maven / Gradle / Ivy

package io.frontroute

import com.raquo.airstream.core.EventStream
import com.raquo.airstream.core.Signal
import com.raquo.airstream.state.Var
import io.frontroute.internal.Util
import io.frontroute.ops.DirectiveOfOptionOps

class Directive[L](
  val tapply: (L => Route) => Route
) {
  self =>

  def flatMap[R](next: L => Directive[R]): Directive[R] = {
    Directive[R] { inner =>
      self.tapply { value => (location, previous, state) =>
        next(value).tapply(inner)(location, previous, state.path(".flatMap"))
      }
    }
  }

  def map[R](f: L => R): Directive[R] =
    Directive[R] { inner =>
      self.tapply { value => (location, previous, state) =>
        val mapped = f(value)
        inner(mapped)(location, previous, state.path(".map").setValue(mapped))
      }
    }

  @inline def some: Directive[Option[L]] = map(Some(_))

  @inline def none[R]: Directive[Option[R]] = mapTo(Option.empty[R])

  @inline def mapTo[R](otherValue: => R): Directive[R] = map(_ => otherValue)

  def &[R](magnet: ConjunctionMagnet[L]): magnet.Out = magnet(this)

  def |[U >: L](other: Directive[L]): Directive[L] = {
    Directive[L] { inner => (ctx, previous, state) =>
      self
        .tapply { value => (ctx, previous, state) =>
          inner(value)(ctx, previous, state.leaveDisjunction())
        }(ctx, previous, state.enterDisjunction())
        .flatMap {
          case complete: RouteResult.Complete => EventStream.fromValue(complete)
          case RouteResult.Rejected =>
            other.tapply { value => (ctx, previous, state) =>
              inner(value)(ctx, previous, state.leaveDisjunction())
            }(ctx, previous, state.enterDisjunction())
        }
    }

  }

  def collect[R](f: PartialFunction[L, R]): Directive[R] =
    Directive[R] { inner =>
      self.tapply { value => (location, previous, state) =>
        if (f.isDefinedAt(value)) {
          val mapped = f(value)
          inner(mapped)(location, previous, state.path(".collect").setValue(mapped))
        } else {
          Util.rejected
        }
      }
    }

  def filter(predicate: L => Boolean): Directive[L] =
    Directive[L] { inner =>
      self.tapply { value => (location, previous, state) =>
        if (predicate(value)) {
          inner(value)(location, previous, state.path(".filter"))
        } else {
          Util.rejected
        }
      }
    }

  def signal: Directive[Signal[L]] =
    new Directive[Signal[L]]({ inner => (ctx, previous, state) =>
      this.tapply {
        value => // TODO figure this out, when this is run, enter is not yet called
          (ctx, previous, state) =>
            val next = state.unsetValue().path(".signal")
            previous.getValue[Var[L]](next.path) match {
              case None =>
                val newVar = Var(value)
                inner(newVar.signal)(ctx, previous, next.setValue(newVar))
              case Some(existingVar) =>
                existingVar.writer.onNext(value)
                inner(existingVar.signal)(ctx, previous, next.setValue(existingVar))
            }
      }(ctx, previous, state)
    })

}

object Directive {

  def apply[L](f: (L => Route) => Route): Directive[L] = {
    new Directive[L](inner =>
      (ctx, previous, state) =>
        f(value =>
          (ctx, previous, state) => {
            inner(value)(ctx, previous, state)
          }
        )(ctx, previous, state)
    )
  }

  implicit def directiveOfOptionSyntax[A](underlying: Directive[Option[A]]): DirectiveOfOptionOps[A] = new DirectiveOfOptionOps[A](underlying)

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy