Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
train-station-tofu/service/src/main/scala/com/psisoyev/train/station/arrival/ArrivalValidator.scala
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
68 lines (56 sloc)
2.38 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.psisoyev.train.station.arrival | |
import cats.{ FlatMap, Monad } | |
import com.psisoyev.train.station.Tracing.ops.TracingOps | |
import com.psisoyev.train.station.arrival.ArrivalValidator.ArrivalError.UnexpectedTrain | |
import com.psisoyev.train.station.arrival.ArrivalValidator.ValidatedArrival | |
import com.psisoyev.train.station.arrival.Arrivals.Arrival | |
import com.psisoyev.train.station.arrival.ExpectedTrains.ExpectedTrain | |
import com.psisoyev.train.station.{ Actual, Tracing, TrainId } | |
import derevo.derive | |
import derevo.tagless.applyK | |
import tofu.higherKind.Mid | |
import tofu.logging.Logging | |
import tofu.syntax.monadic._ | |
import tofu.syntax.monoid.TofuSemigroupOps | |
import tofu.syntax.raise._ | |
import tofu.{ Handle, Raise } | |
import scala.util.control.NoStackTrace | |
@derive(applyK) | |
trait ArrivalValidator[F[_]] { | |
def validate(arrival: Arrival): F[ValidatedArrival] | |
} | |
object ArrivalValidator { | |
sealed trait ArrivalError extends NoStackTrace | |
object ArrivalError { | |
type Handling[F[_]] = Handle[F, ArrivalError] | |
type Raising[F[_]] = Raise[F, ArrivalError] | |
case class UnexpectedTrain(id: TrainId) extends ArrivalError | |
} | |
case class ValidatedArrival(trainId: TrainId, time: Actual, expectedTrain: ExpectedTrain) | |
private class Log[F[_]: FlatMap: Logging] extends ArrivalValidator[Mid[F, *]] { | |
def validate(arrival: Arrival): Mid[F, ValidatedArrival] = { validation => | |
F.info(s"Validating $arrival") *> validation <* F.info(s"Train ${arrival.trainId} validated") | |
} | |
} | |
private class Trace[F[_]: Tracing] extends ArrivalValidator[Mid[F, *]] { | |
def validate(arrival: Arrival): Mid[F, ValidatedArrival] = _.traced("train arrival: validation") | |
} | |
private class Impl[F[_]: Monad: ArrivalError.Raising](expectedTrains: ExpectedTrains[F]) extends ArrivalValidator[F] { | |
override def validate(arrival: Arrival): F[ValidatedArrival] = | |
expectedTrains | |
.get(arrival.trainId) | |
.flatMap { train => | |
train | |
.map(ValidatedArrival(arrival.trainId, arrival.time, _)) | |
.orRaise(UnexpectedTrain(arrival.trainId)) | |
} | |
} | |
def make[F[_]: Monad: Logging: ArrivalError.Raising: Tracing]( | |
expectedTrains: ExpectedTrains[F] | |
): ArrivalValidator[F] = { | |
val service = new Impl[F](expectedTrains) | |
val log: ArrivalValidator[Mid[F, *]] = new Log[F] | |
val trace: ArrivalValidator[Mid[F, *]] = new Trace[F] | |
(log |+| trace).attach(service) | |
} | |
} |