cats.data.Chain Scala Examples

The following examples show how to use cats.data.Chain. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example.
Example 1
Source File: UnusedLetCheck.scala    From bosatsu   with Apache License 2.0 5 votes vote down vote up
package org.bykn.bosatsu

import cats.Applicative
import cats.data.{Chain, Validated, ValidatedNec, Writer, NonEmptyChain}
import cats.implicits._

import Expr._
import Identifier.Bindable

object UnusedLetCheck {

  private[this] val ap = Applicative[Writer[Chain[(Bindable, Region)], ?]]
  private[this] val empty: Writer[Chain[(Bindable, Region)], Set[Bindable]] = ap.pure(Set.empty)

  private[this] def checkArg(arg: Bindable, reg: => Region, w: Writer[Chain[(Bindable, Region)], Set[Bindable]]) =
    w.flatMap { free =>
      if (free(arg)) ap.pure(free - arg)
      else {
        // this arg is free:
        ap.pure(free).tell(Chain.one((arg, reg)))
      }
    }

  private[this] def loop[A: HasRegion](e: Expr[A]): Writer[Chain[(Bindable, Region)], Set[Bindable]] =
    e match {
      case Annotation(expr, _, _) =>
        loop(expr)
      case AnnotatedLambda(arg, _, expr, _) =>
        checkArg(arg, HasRegion.region(e), loop(expr))
      case Lambda(arg, expr, _) =>
        checkArg(arg, HasRegion.region(e), loop(expr))
      case Let(arg, expr, in, rec, _) =>
        val exprCheck = loop(expr)
        val exprRes =
          // if it is recursive, it is definitely used, because
          // that is automatically applied in source conversions
          if (rec.isRecursive) exprCheck.map(_ - arg) else exprCheck
        val inCheck = checkArg(arg, HasRegion.region(e), loop(in))
        (exprRes, inCheck).mapN(_ ++ _)
      case Var(None, name: Bindable, _) =>
        // this is a free variable:
        ap.pure(Set(name))
      case Var(_, _, _) | Literal(_, _) => empty
      case App(fn, arg, _) =>
        (loop(fn), loop(arg)).mapN(_ ++ _)
      case Match(arg, branches, _) =>
        // TODO: patterns need their own region
        val argCheck = loop(arg)
        val bcheck = branches.traverse { case (pat, expr) =>
          val region = HasRegion.region(expr)
          loop(expr).flatMap { frees =>
            val thisPatNames = pat.names
            val unused = thisPatNames.filterNot(frees)
            val nextFrees = frees -- thisPatNames

            ap.pure(nextFrees).tell(Chain.fromSeq(unused.map((_, region))))
          }
        }
        .map(_.combineAll)

      (argCheck, bcheck).mapN(_ ++ _)
    }

  
  def freeBound[A](e: Expr[A]): Set[Bindable] =
    loop(e)(HasRegion.instance(_ => Region(0, 0))).run._2
} 
Example 2
Source File: FoldableSuite.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package tofu.syntax

import cats.data.Writer
import tofu.syntax.monadic._
import cats.data.Chain
import tofu.syntax.foldable._
import cats.instances.stream._
import org.scalatest.flatspec.AnyFlatSpec

class FoldableSuite extends AnyFlatSpec {
  def add(s: Int, x: Int) = x > 0 whenOpt Writer.tell(Chain(s)).as(s + x)
  def look(x: Int)        = x > 0 whenOpt Writer.tell(Chain(x)).as(x.toString)
  val elems               = Stream.range(1, 10) #::: Stream.from(-1, -1)

  "foldWhileM" should "scan elements" in
    assert(
      elems.foldWhileM(0)(add).written.toList === List.range(1, 9).scanLeft(0)(_ + _)
    )

  "takeWhileM" should "filter elements" in
    assert(elems.takeWhileM(look).run === ((Chain.fromSeq(1 to 9), List.range(1, 10).map(_.toString))))
} 
Example 3
Source File: DoMonadExamples.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package examples

import cats.data.Chain
import tofu.alias.Do

class DoMonadExamples {
  def bar[F[_]: Do, A, B, C](f: A => F[B], g: B => F[C], h: C => F[Unit], fa: F[A]) =
    for {
      x <- fa
      y <- f(x)
      z <- g(y).flatTap(h)
    } yield (x, y, z)

  def bar2[F[_]: Do, A, B, C, D](f: F[(A, B) => C], g: F[C => D], fa: F[A], fb: F[B]) =
    g <*> f.ap2(fa, fb)

  def covCheck[A, B, C, E, F[_], F1[x] >: F[x], F2[x] >: F1[x]: Do](
      fa: F[A],
      f: A => F1[B],
      g: B => F2[C],
  ) = for {
    x <- fa
    y <- f(x)
    z <- g(y)
  } yield (x, y, z)

  def eitherCheck2[A, B <: A, C <: A, E, E1 <: E, E2 <: E, E3 <: E](
      fb: Either[E1, Boolean],
      fx: Either[E2, B],
      fy: Either[E3, C],
  ) = {
    val res                    = fb.ifM(fx, fy)
    val resCheck: Either[E, A] = res
    resCheck
  }

  def doStaticSyntaxCheck[F[_]: Do, A](a: A): F[A] = {
    Do[F].unit
    Do[F].pure(a)
  }

  def doLazySyntaxCheck2[F[_]: Do, A](fa: => F[A], fb: F[Boolean]) = {
    Do ~ fa when 1 == 2
    Do ~ fa unlessOpt 1 == 2
    Do ~ fa whenM fb
    Do ~ fa unlessOptM fb
  }

  def doLoopSyntaxCheck = {
    Do loop 1 iterate (x => List(x * 2, x * 3)) whileM (_ < 100)
    Do loop 1 iterate (x => List(x * 2, x * 3)) untilM (_ > 100)
    Do loop 27 tailRecM (x =>
      (
        Chain.one(x),
        if (x % 2 == 0) Left(x / 2)
        else if (x > 1) Left(x * 3 + 1)
        else Right(())
      )
    )
  }
} 
Example 4
Source File: FoldableSuite.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package tofu.syntax

import cats.data.Writer
import tofu.syntax.monadic._
import cats.data.Chain
import tofu.syntax.foldable._
import cats.instances.lazyList._
import org.scalatest.flatspec.AnyFlatSpec
import scala.collection.compat._

class FoldableSuite extends AnyFlatSpec {
  def add(s: Int, x: Int) = x > 0 whenOpt Writer.tell(Chain(s)).as(s + x)
  def look(x: Int)        = x > 0 whenOpt Writer.tell(Chain(x)).as(x.toString)
  val elems               = LazyList.range(1, 10) #::: LazyList.from(-1, -1)

  "foldWhileM" should "scan elements" in
    assert(
      elems.foldWhileM(0)(add).written.toList === List.range(1, 9).scanLeft(0)(_ + _)
    )

  "takeWhileM" should "filter elements" in
    assert(elems.takeWhileM(look).run === ((Chain.fromSeq(1 to 9), List.range(1, 10).map(_.toString))))
} 
Example 5
Source File: Mid.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package tofu.higherKind
import cats.data.Chain
import cats.tagless.ApplyK
import cats.{Monoid, MonoidK, Semigroup}
import tofu.higherKind.Mid.MidCompose
import tofu.syntax.funk.funK
import tofu.syntax.monoidalK._

trait Mid[F[_], A] {
  def apply(fa: F[A]): F[A]
  @inline def attach(fa: F[A]): F[A] = apply(fa)

  def compose(that: Mid[F, A]): Mid[F, A] = that.andThen(this)
  def andThen(that: Mid[F, A]): Mid[F, A] = MidCompose(Chain(this, that))
}

object Mid extends MidInstances {
  def point[F[_]]: Point[Mid[F, *]] = new Point[Mid[F, *]] {
    override def point[A]: Mid[F, A] = x => x
  }

  
  def attach[U[f[_]]: ApplyK, F[_]](up: U[Mid[F, *]])(alg: U[F]): U[F] = up.attach(alg)

  implicit final class TofuMidAlgebraSyntax[F[_], U[f[_]]](private val self: U[Mid[F, *]]) extends AnyVal {
    def attach(alg: U[F])(implicit U: ApplyK[U]): U[F] =
      U.map2K(alg, self)(funK(t2k => t2k.second(t2k.first)))
  }

  private final case class MidCompose[F[_], A](elems: Chain[Mid[F, A]]) extends Mid[F, A] {
    override def apply(fa: F[A]): F[A]               = elems.foldLeft(fa)((x, m) => m(x))
    override def compose(that: Mid[F, A]): Mid[F, A] = that match {
      case MidCompose(es) => MidCompose(elems ++ es)
      case _              => MidCompose(elems :+ that)
    }
    override def andThen(that: Mid[F, A]): Mid[F, A] = that match {
      case MidCompose(es) => MidCompose(es ++ elems)
      case _              => MidCompose(that +: elems)
    }
  }

}
trait MidInstances extends MidInstances1 {
  implicit def midMonoidK[F[_]]: MonoidK[Mid[F, *]] = new MidMonoidK[F]

  implicit def midAlgebraMonoid[F[_], U[f[_]]: MonoidalK]: Monoid[U[Mid[F, *]]] = new MidAlgebraMonoid[F, U]
}

trait MidInstances1 {
  implicit def midAlgebraSemigroup[F[_], U[f[_]]: ApplyK]: Semigroup[U[Mid[F, *]]] = new MidAlgebraSemigroup[F, U]
}

class MidMonoidK[F[_]] extends MonoidK[Mid[F, *]] {
  def empty[A]: Mid[F, A]                                = fa => fa
  def combineK[A](x: Mid[F, A], y: Mid[F, A]): Mid[F, A] = fa => x(y(fa))
}

class MidAlgebraMonoid[F[_], U[f[_]]: MonoidalK] extends MidAlgebraSemigroup[F, U] with Monoid[U[Mid[F, *]]] {
  def empty: U[Mid[F, *]] = Mid.point[F].pureK[U]
}

class MidAlgebraSemigroup[F[_], U[f[_]]: ApplyK](implicit U: ApplyK[U]) extends Semigroup[U[Mid[F, *]]] {
  def combine(x: U[Mid[F, *]], y: U[Mid[F, *]]): U[Mid[F, *]] = U.map2K(x, y)(funK(t2 => fa => t2.first(t2.second(fa))))
} 
Example 6
Source File: EventsourcedState.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.runtime

import aecor.data.Folded.Next
import aecor.data.{ ActionT, Fold, Folded }
import aecor.runtime.Eventsourced.Versioned
import cats.data.{ Chain, NonEmptyChain }
import cats.effect.Sync
import cats.implicits._

private[aecor] trait EventsourcedState[F[_], K, S, E] {
  def recover(key: K, snapshot: Option[Versioned[S]]): F[Versioned[S]]
  def run[A](key: K, state: Versioned[S], action: ActionT[F, S, E, A]): F[(Versioned[S], A)]
}

private[aecor] object EventsourcedState {
  def apply[F[_]: Sync, K, E, S](fold: Fold[Folded, S, E],
                                 journal: EventJournal[F, K, E]): EventsourcedState[F, K, S, E] =
    new DefaultEventsourcedState(fold, journal)
}

private[aecor] final class DefaultEventsourcedState[F[_], K, E, S](
  fold: Fold[Folded, S, E],
  journal: EventJournal[F, K, E]
)(implicit F: Sync[F])
    extends EventsourcedState[F, K, S, E] {

  private val versionedFold = Versioned.fold(fold)

  override def recover(key: K, snapshot: Option[Versioned[S]]): F[Versioned[S]] = {
    val from = snapshot.getOrElse(versionedFold.initial)
    journal
      .read(key, from.version)
      .evalScan(from) { (s, e) =>
        versionedFold.reduce(s, e.payload) match {
          case Next(a) =>
            F.pure(a)
          case Folded.Impossible =>
            F.raiseError(new IllegalStateException(s"Failed to apply event [$e] to [$s]"))
        }
      }
      .compile
      .lastOrError
  }

  override def run[A](key: K,
                      state: Versioned[S],
                      action: ActionT[F, S, E, A]): F[(Versioned[S], A)] =
    for {
      result <- action
                 .expand[Versioned[S]]((versioned, s) => versioned.copy(value = s))(_.value)
                 .zipWithRead
                 .run(versionedFold.init(state))
      (es, (a, nextState)) <- result match {
                               case Next(a) => a.pure[F]
                               case Folded.Impossible =>
                                 F.raiseError[(Chain[E], (A, Versioned[S]))](
                                   new IllegalArgumentException(
                                     s"Failed to run action [$action] against state [$state]"
                                   )
                                 )
                             }
      _ <- NonEmptyChain
            .fromChain(es)
            .traverse_(nes => journal.append(key, state.version + 1, nes))
    } yield (nextState, a)
} 
Example 7
Source File: EventsourcedBehavior.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.data

import aecor.Has
import aecor.arrow.Invocation
import cats.{ Monad, ~> }
import cats.data.{ Chain, EitherT }
import cats.tagless.FunctorK
import cats.tagless.syntax.functorK._

final case class EventsourcedBehavior[M[_[_]], F[_], S, E](actions: M[ActionT[F, S, E, *]],
                                                           fold: Fold[Folded, S, E]) {
  def enrich[Env](f: F[Env])(implicit M: FunctorK[M],
                             F: Monad[F]): EventsourcedBehavior[M, F, S, Enriched[Env, E]] =
    EventsourcedBehavior(
      actions.mapK(ActionT.sampleK[F, S, E, Env, Enriched[Env, E]](f)(Enriched(_, _))(_.event)),
      fold.contramap(_.event)
    )

  def mapK[G[_]](fg: F ~> G)(implicit M: FunctorK[M]): EventsourcedBehavior[M, G, S, E] =
    copy(actions.mapK(λ[ActionT[F, S, E, *] ~> ActionT[G, S, E, *]](_.mapK(fg))))

  def run[A](state: S, invocation: Invocation[M, A]): F[Folded[(Chain[E], A)]] =
    invocation
      .run(actions)
      .run(fold.init(state))
}

object EventsourcedBehavior extends EventsourcedBehaviourIntances {
  def rejectable[M[_[_]], F[_], S, E, R](
    actions: M[EitherT[ActionT[F, S, E, *], R, *]],
    fold: Fold[Folded, S, E]
  ): EventsourcedBehavior[EitherK[M, R, *[_]], F, S, E] =
    EventsourcedBehavior[EitherK[M, R, *[_]], F, S, E](EitherK(actions), fold)
}

trait EventsourcedBehaviourIntances {
  implicit def eventsourcedBehaviourFunctorKInstance[M[_[_]]: FunctorK, S, E]
    : FunctorK[EventsourcedBehavior[M, *[_], S, E]] =
    new FunctorK[EventsourcedBehavior[M, *[_], S, E]] {
      def mapK[F[_], G[_]](a: EventsourcedBehavior[M, F, S, E])(
        f: F ~> G
      ): EventsourcedBehavior[M, G, S, E] = a.mapK(f)
    }
}

final case class Enriched[M, E](metadata: M, event: E)
object Enriched {
  implicit def hasMetadata[M, E, X](implicit M: Has[M, X]): Has[Enriched[M, E], X] =
    M.contramap(_.metadata)
  implicit def hasEvent[M, E, X](implicit E: Has[E, X]): Has[Enriched[M, E], X] =
    E.contramap(_.event)
} 
Example 8
Source File: ActionTSpec.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.tests

import aecor.data.Folded.Next
import aecor.data.{ ActionT, Fold, Folded }
import aecor.runtime.Eventsourced.Versioned
import cats.Id
import cats.data.{ Chain, NonEmptyChain }
import cats.instances.string._
import cats.syntax.either._
import cats.syntax.flatMap._
import cats.syntax.functor._
import org.scalatest.funsuite.AnyFunSuite

class ActionTSpec extends AnyFunSuite {
  def append(s: String): ActionT[Id, String, String, Unit] = ActionT.append(NonEmptyChain.one(s))
  def read[S]: ActionT[Id, S, String, S] = ActionT.read
  def pure[A](a: A): ActionT[Id, String, String, A] = ActionT.pure(a)
  def fold(s: String) = Fold(s, (l: String, r: String) => Folded.next(l ++ r))
  def run[A](action: ActionT[Id, String, String, A]): Folded[(Chain[String], A)] =
    action.run(fold(""))

  test("ActionT.read associativity") {
    val n1 @ Next((es, out)) = run(append("a") >> (append("b") >> read))
    val n2 = run(append("a") >> append("b") >> read)
    assert(es === Chain("a", "b"))
    assert(out === "ab")
    assert(n1 === n2)
  }

  test("xmapState") {
    val Next((es, (out, versioned))) =
      (append("a") >> append("b") >> read)
        .expand[Versioned[String]]((s, v) => s.copy(value = v))(_.value)
        .zipWithRead
        .run(Versioned.fold(fold("")))
    println(versioned)
    assert(versioned.version == 2)
    assert(es === Chain("a", "b"))
    assert(out === "ab")
  }

  test("tailRecM") {
    val x: ActionT[Id, String, String, Unit] = 0.tailRecM { c =>
      if (c < 50000) {
        append(s"$c").as((c + 1).asLeft[Unit])
      } else {
        pure(().asRight)
      }
    }

    val Next((es, _)) = run(x)
    assert(es.size == 50000)
  }

} 
Example 9
Source File: ScheduleBucketSpec.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.tests

import java.time._

import aecor.data.Folded
import aecor.schedule.ScheduleEvent.{ ScheduleEntryAdded, ScheduleEntryFired }
import aecor.schedule.{ DefaultScheduleBucket, ScheduleState }
import cats.Id
import cats.data.Chain
import org.scalatest.matchers.should.Matchers
import org.scalatest.flatspec.AnyFlatSpec

class ScheduleBucketSpec extends AnyFlatSpec with Matchers with StrictCatsEquality {
  val clock = Clock.fixed(Instant.now, ZoneId.systemDefault())
  val aggregate = DefaultScheduleBucket.behavior[Id](ZonedDateTime.now(clock)).actions

  "ScheduleBucket" should "fire entry when due date is before now" in {
    val handler = aggregate.addScheduleEntry(
      "entryId",
      "correlation",
      LocalDateTime.now(clock).minusSeconds(10)
    )

    val Folded.Next((events, _)) = handler.run(ScheduleState.fold)
    assert(
      events ===
        Chain(
          ScheduleEntryAdded(
            "entryId",
            "correlation",
            LocalDateTime.now(clock).minusSeconds(10),
            Instant.now(clock)
          ),
          ScheduleEntryFired("entryId", "correlation", Instant.now(clock))
        )
    )
  }
  it should "not fire entry when due date is after now" in {
    val handler =
      aggregate.addScheduleEntry("entryId", "correlation", LocalDateTime.now(clock).plusSeconds(10))

    val Folded.Next((events, _)) = handler.run(ScheduleState.fold)
    assert(
      events ===
        Chain(
          ScheduleEntryAdded(
            "entryId",
            "correlation",
            LocalDateTime.now(clock).plusSeconds(10),
            Instant.now(clock)
          )
        )
    )
  }
} 
Example 10
Source File: StateEventJournal.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.testkit

import aecor.data._
import aecor.runtime.EventJournal
import aecor.testkit.StateEventJournal.State
import cats.Monad
import cats.data.{ Chain, NonEmptyChain }
import cats.implicits._
import cats.mtl.MonadState
import fs2._
import monocle.Lens

object StateEventJournal {
  final case class State[K, E](eventsByKey: Map[K, Chain[E]],
                               eventsByTag: Map[EventTag, Chain[EntityEvent[K, E]]],
                               consumerOffsets: Map[(EventTag, ConsumerId), Int]) {
    def getConsumerOffset(tag: EventTag, consumerId: ConsumerId): Int =
      consumerOffsets.getOrElse(tag -> consumerId, 0)

    def setConsumerOffset(tag: EventTag, consumerId: ConsumerId, offset: Int): State[K, E] =
      copy(consumerOffsets = consumerOffsets.updated(tag -> consumerId, offset))

    def getEventsByTag(tag: EventTag, offset: Int): Chain[(Int, EntityEvent[K, E])] = {
      val stream = eventsByTag
        .getOrElse(tag, Chain.empty)
        .mapWithIndex((e, i) => (i + 1, e))
        .toList
        .drop(offset)
      Chain.fromSeq(stream)
    }

    def readEvents(key: K): Chain[EntityEvent[K, E]] =
      eventsByKey
        .getOrElse(key, Chain.empty)
        .mapWithIndex((e, idx) => EntityEvent(key, idx + 1L, e))

    def appendEvents(key: K, offset: Long, events: NonEmptyChain[TaggedEvent[E]]): State[K, E] = {
      val updatedEventsById = eventsByKey
        .updated(key, eventsByKey.getOrElse(key, Chain.empty) ++ events.map(_.event).toChain)

      val newEventsByTag: Map[EventTag, Chain[EntityEvent[K, E]]] = events.toChain.zipWithIndex
        .flatMap {
          case (e, idx) =>
            Chain.fromSeq(e.tags.toSeq).map(t => t -> EntityEvent(key, idx + offset, e.event))
        }
        .groupBy(_._1)
        .map {
          case (key, value) =>
            key -> value.map(_._2).toChain
        }

      copy(
        eventsByKey = updatedEventsById,
        eventsByTag =
          eventsByTag |+| newEventsByTag
      )
    }
  }

  object State {
    def init[I, E]: State[I, E] = State(Map.empty, Map.empty, Map.empty)
  }

  def apply[F[_]: Monad: MonadState[*[_], A], K, A, E](
    lens: Lens[A, State[K, E]],
    tagging: Tagging[K]
  ): StateEventJournal[F, K, A, E] =
    new StateEventJournal(lens, tagging)

}

final class StateEventJournal[F[_]: Monad, K, S, E](lens: Lens[S, State[K, E]], tagging: Tagging[K])(
  implicit MS: MonadState[F, S]
) extends EventJournal[F, K, E] {
  private final val F = lens.transformMonadState(MonadState[F, S])

  override def append(key: K, offset: Long, events: NonEmptyChain[E]): F[Unit] =
    F.modify(_.appendEvents(key, offset, events.map(e => TaggedEvent(e, tagging.tag(key)))))

  override def read(key: K, offset: Long): Stream[F, EntityEvent[K, E]] =
    Stream
      .eval(F.inspect(_.readEvents(key)))
      .flatMap(x => Stream(x.toVector: _*))
      .drop(offset)

  def currentEventsByTag(tag: EventTag,
                         consumerId: ConsumerId): Stream[F, Committable[F, EntityEvent[K, E]]] =
    for {
      state <- Stream.eval(F.get)
      committedOffset = state.getConsumerOffset(tag, consumerId)
      result = state.getEventsByTag(tag, committedOffset)
      a <- Stream(result.toVector: _*).map {
            case (offset, e) =>
              Committable(F.modify(_.setConsumerOffset(tag, consumerId, offset)), e)
          }
    } yield a

}

final case class TaggedEvent[E](event: E, tags: Set[EventTag]) 
Example 11
Source File: TestConsole.scala    From console4cats   with Apache License 2.0 5 votes vote down vote up
package cats.effect.test

import cats._
import cats.data.Chain
import cats.effect.concurrent.Ref
import cats.effect.{ Console, Sync }
import cats.syntax.functor._
import cats.syntax.show._

private class TestConsole[F[_]: Applicative](
    outLines: Ref[F, Chain[String]],
    outWords: Ref[F, Chain[String]],
    outErrors: Ref[F, Chain[String]],
    val readLn: F[String]
) extends Console[F] {

  override def putStrLn[A: Show](a: A): F[Unit] =
    outLines.update(_.append(a.show))

  override def putStr[A: Show](a: A): F[Unit] =
    outWords.update(_.append(a.show))

  override def putError[A: Show](a: A): F[Unit] =
    outErrors.update(_.append(a.show))
}

object TestConsole {

  
    def sequenceAndDefault[F[_]: Sync](
        inputs: Chain[String],
        default: String
    ): F[F[String]] =
      Ref[F].of(inputs).map {
        _.modify {
          _.uncons match {
            case Some((head, tail)) => (tail, head)
            case None               => (Chain.nil, default)
          }
        }
      }
  }
} 
Example 12
Source File: model.scala    From sonar-scala   with GNU Lesser General Public License v3.0 5 votes vote down vote up
package com.mwz.sonar.scala.metadata

import cats.data.Chain
import cats.data.NonEmptyChain
import enumeratum._
import io.circe._
import io.circe.generic.JsonCodec
import io.circe.generic.encoding._
import org.sonar.api.server.rule.RuleParamType
import shapeless._
import shapeless.record._

@JsonCodec
final case class RulesRepository(
  key: String,
  name: String,
  rules: NonEmptyChain[Rule]
)

object Rule {
  implicit val ruleEncoder: Encoder[Rule] =
    ReprObjectEncoder.deriveReprAsObjectEncoder.contramap { rule =>
      LabelledGeneric[Rule]
        .to(rule)
        .-(Symbol("sonarMdDescription"))
        .renameField(Symbol("mdDescription"), Symbol("description"))
    }
}

@JsonCodec(decodeOnly = true)
final case class Rule(
  key: String,
  name: String,
  mdDescription: String,
  sonarMdDescription: String,
  severity: Severity,
  template: Boolean,
  params: Chain[Param]
)

@JsonCodec
final case class Param(
  name: String,
  typ: ParamType,
  description: String,
  default: String
)

sealed trait ParamType extends EnumEntry
object ParamType extends Enum[ParamType] with CirceEnum[ParamType] with CatsEnum[ParamType] {
  final case object String extends ParamType
  final case object Text extends ParamType
  final case object Boolean extends ParamType
  final case object Integer extends ParamType
  final case object Float extends ParamType
  val values = findValues

  implicit class ParamTypeSyntax(private val paramType: ParamType) extends AnyVal {
    def asSonarRuleParamType: RuleParamType =
      paramType match {
        case String  => RuleParamType.STRING
        case Text    => RuleParamType.TEXT
        case Boolean => RuleParamType.BOOLEAN
        case Integer => RuleParamType.INTEGER
        case Float   => RuleParamType.FLOAT
      }
  }
}

sealed trait Severity extends EnumEntry
object Severity extends Enum[Severity] with CirceEnum[Severity] with CatsEnum[Severity] {
  final case object Info extends Severity
  final case object Minor extends Severity
  final case object Major extends Severity
  final case object Critical extends Severity
  final case object Blocker extends Severity
  val values = findValues
} 
Example 13
Source File: BasePath.scala    From fs2-blobstore   with Apache License 2.0 5 votes vote down vote up
package blobstore

import java.net.URI
import java.time.Instant

import cats.data.Chain

class BasePath private[blobstore] (
  val root: Option[String],
  val pathFromRoot: Chain[String],
  val fileName: Option[String],
  val size: Option[Long],
  val isDir: Option[Boolean],
  val lastModified: Option[Instant]
) extends Path

object BasePath {
  val empty: BasePath = BasePath(None, Chain.empty, None, None, Some(true), None)

  def apply(
    root: Option[String],
    pathFromRoot: Chain[String],
    fileName: Option[String],
    size: Option[Long],
    isDir: Option[Boolean],
    lastModified: Option[Instant]
  ): BasePath = new BasePath(root, pathFromRoot, fileName, size, isDir, lastModified)

  def fromString(s: String, forceRoot: Boolean): Option[BasePath] =
    if (s.isEmpty) Some(empty)
    else
      scala.util.Try(URI.create(s.replaceAll(" ", "%20"))).toOption.flatMap { uri =>
        val maybePath = Option(uri.getPath)
        val pathSegments =
          maybePath.map(_.split("/").filterNot(_.isEmpty)).getOrElse(Array.empty)
        val authority = Option(uri.getAuthority)
        val root =
          authority.orElse(if (forceRoot) pathSegments.headOption else None)
        if ((root.isEmpty && forceRoot) || root.exists(_.isEmpty))
          Option.empty[BasePath]
        else {
          val (pathFromRoot, fileName) = {
            val ps = if (root.isDefined && authority.isEmpty) pathSegments.drop(1) else pathSegments
            val fn = if (maybePath.exists(_.endsWith("/"))) None else ps.lastOption
            val ss = fn.fold(ps)(_ => ps.init)
            Chain.fromSeq(ss.toIndexedSeq) -> fn
          }
          Some(
            BasePath(root, pathFromRoot, fileName, None, None, None)
          )
        }
      }
} 
Example 14
Source File: S3Path.scala    From fs2-blobstore   with Apache License 2.0 5 votes vote down vote up
package blobstore
package s3

import java.time.Instant

import cats.data.Chain

case class S3Path(
  bucket: String,
  key: String,
  meta: Option[S3MetaInfo]
) extends Path {

  val root: Option[String] = Some(bucket)
  val pathFromRoot: Chain[String] = {
    val split = key.split('/').filter(_.nonEmpty)
    Chain.fromSeq(meta.fold(split)(_ => split.dropRight(1)).toIndexedSeq)
  }

  val fileName: Option[String] =
    meta.fold(Option.empty[String])(_ =>
      key.split('/').filter(_.nonEmpty).lastOption.map(_ ++ (if (key.endsWith("/")) "/" else ""))
    )

  val size: Option[Long] = meta.flatMap(_.size)

  val isDir: Option[Boolean] = Some(meta.fold(true)(_ => false))
  val lastModified: Option[Instant] =
    meta.flatMap(_.lastModified)
}

object S3Path {
  def narrow(p: Path): Option[S3Path] = p match {
    case s3Path: S3Path => Some(s3Path)
    case _              => None
  }
} 
Example 15
Source File: AzurePath.scala    From fs2-blobstore   with Apache License 2.0 5 votes vote down vote up
package blobstore.azure

import java.time.Instant

import blobstore.Path
import cats.data.Chain
import com.azure.storage.blob.models.BlobItemProperties

case class AzurePath(container: String, blob: String, properties: Option[BlobItemProperties], meta: Map[String, String])
  extends Path {
  val root: Option[String]   = Some(container)
  val isDir: Option[Boolean] = Some(properties.isEmpty)
  val pathFromRoot: Chain[String] = {
    val segments = {
      val split = blob.split('/').filter(_.nonEmpty)
      if (isDir.contains(true)) split else split.dropRight(1)
    }
    Chain.fromSeq(segments.toIndexedSeq)
  }
  val fileName: Option[String] =
    if (isDir.contains(true)) None
    else {
      blob.split('/').filter(_.nonEmpty).lastOption.map(_ ++ (if (blob.endsWith("/")) "/" else ""))
    }
  val size: Option[Long] =
    properties.flatMap(bip => Option(bip.getContentLength).map(_.toLong))
  val lastModified: Option[Instant] =
    properties.flatMap(bp => Option(bp.getLastModified).map(_.toInstant))
}

object AzurePath {
  def narrow(p: Path): Option[AzurePath] = p match {
    case azureP: AzurePath => Some(azureP)
    case _                 => None
  }
} 
Example 16
Source File: GcsPath.scala    From fs2-blobstore   with Apache License 2.0 5 votes vote down vote up
package blobstore
package gcs

import java.time.Instant

import cats.data.Chain
import com.google.cloud.storage.BlobInfo

case class GcsPath(blobInfo: BlobInfo) extends Path {
  private val maybeFlatName = Option(blobInfo.getName)

  val root: Option[String]   = Option(blobInfo.getBucket)
  val isDir: Option[Boolean] = Option(blobInfo.isDirectory)
  val size: Option[Long]     = Option(blobInfo.getSize: java.lang.Long).map(_.toLong)
  val lastModified: Option[Instant] =
    Option(blobInfo.getUpdateTime: java.lang.Long).map(millis => Instant.ofEpochMilli(millis))
  val fileName: Option[String] =
    if (isDir.contains(true)) None
    else
      maybeFlatName.flatMap(flatName =>
        flatName.split('/').filter(_.nonEmpty).lastOption.map(_ ++ (if (flatName.endsWith("/")) "/" else ""))
      )

  val pathFromRoot: Chain[String] = {
    val segments = maybeFlatName.fold(Array.empty[String]) { flatName =>
      val split = flatName.split('/').filter(_.nonEmpty)
      if (isDir.contains(true)) split else split.dropRight(1)
    }
    Chain.fromSeq(segments.toIndexedSeq)
  }
}

object GcsPath {
  def narrow(p: Path): Option[GcsPath] = p match {
    case gcsP: GcsPath => Some(gcsP)
    case _             => None
  }
} 
Example 17
Source File: BoxPath.scala    From fs2-blobstore   with Apache License 2.0 5 votes vote down vote up
package blobstore
package box

import java.time.Instant

import cats.data.Chain
import com.box.sdk.{BoxFile, BoxFolder}

import scala.jdk.CollectionConverters._

class BoxPath private[box] (
  val root: Option[String],
  private[box] val rootId: Option[String],
  val fileOrFolder: Either[BoxFile#Info, BoxFolder#Info]
) extends Path {
  val pathFromRoot: Chain[String] = {
    val withRoot = fileOrFolder.fold(_.getPathCollection, _.getPathCollection).asScala
    val pathToSelf =
      Chain.fromSeq(
        withRoot.drop(withRoot.lastIndexWhere(info => rootId.contains(info.getID)) + 1).toIndexedSeq
      )
    fileOrFolder.fold(_ => pathToSelf, folder => pathToSelf.append(folder)).map(_.getName)
  }
  val fileName: Option[String]      = fileOrFolder.fold(file => Some(file.getName), _ => None)
  val size: Option[Long]            = fileOrFolder.fold(file => Option(file.getSize), _ => None)
  val isDir: Option[Boolean]        = Some(fileOrFolder.isRight)
  val lastModified: Option[Instant] = Option(fileOrFolder.fold(_.getModifiedAt, _.getModifiedAt)).map(_.toInstant)
}

object BoxPath {
  def narrow(p: Path): Option[BoxPath] = p match {
    case bp: BoxPath => Some(bp)
    case _           => None
  }

  private[box] def rootFilePath(p: Path): List[String] = {
    val withRoot = p.root.fold(p.pathFromRoot)(p.pathFromRoot.prepend)
    p.fileName.fold(withRoot)(withRoot.append).filter(_.nonEmpty).toList
  }

  private[box] def parentPath(path: Path): List[String] =
    rootFilePath(path).dropRight(1)
} 
Example 18
Source File: ChainBenchmarks.scala    From zio   with Apache License 2.0 5 votes vote down vote up
package zio.chunks

import java.util.concurrent.TimeUnit

import cats.data.Chain
import org.openjdk.jmh.annotations._

import zio.Chunk

@State(Scope.Thread)
@BenchmarkMode(Array(Mode.AverageTime))
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 0)
@Measurement(iterations = 1)
class ChainBenchmarks {

  val largeChain: Chain[Int] =
    (0 to 1000).foldLeft(Chain.empty[Int])((acc, _) => acc ++ Chain.fromSeq(0 to 1000))

  val largeChunk =
    (0 to 1000).foldLeft[Chunk[Int]](Chunk.empty)((acc, _) => acc ++ Chunk.fromIterable(0 to 1000))

  val largeVector: Vector[Int] =
    (0 to 1000000).toVector

  val largeList: List[Int] =
    (0 to 1000000).toList

  @Benchmark
  def foldLeftLargeChain: Int =
    largeChain.foldLeft(0)(_ + _)
  @Benchmark
  def foldLeftLargeChunk: Int =
    largeChunk.foldLeft(0)(_ + _)
  @Benchmark
  def foldLeftLargeVector: Int =
    largeVector.foldLeft(0)(_ + _)
  @Benchmark
  def foldLeftLargeList: Int =
    largeList.foldLeft(0)(_ + _)

  @Benchmark
  def mapLargeChain: Chain[Int] =
    largeChain.map(_ + 1)
  @Benchmark
  def mapLargeChunk: Chunk[Int] =
    largeChunk.map(_ + 1)
  @Benchmark
  def mapLargeVector: Vector[Int] =
    largeVector.map(_ + 1)
  @Benchmark
  def mapLargeList: List[Int] =
    largeList.map(_ + 1)
} 
Example 19
Source File: ScalastyleRulesReposotorySpec.scala    From sonar-scala   with GNU Lesser General Public License v3.0 5 votes vote down vote up
package com.mwz.sonar.scala.metadata
package scalastyle

import cats.data.Chain
import cats.data.NonEmptyChain
import enumeratum.scalacheck._
import org.scalacheck.Prop._
import org.scalacheck.Prop._
import org.scalacheck.ScalacheckShapeless._
import org.scalacheck._
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import org.scalatestplus.scalacheck.Checkers
import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks

class ScalastyleRulesRepositorySpec
    extends AnyFlatSpec
    with Matchers
    with ScalaCheckDrivenPropertyChecks
    with Checkers {

  implicit def chainArbOf[T](implicit a: Arbitrary[T]): Arbitrary[Chain[T]] =
    Arbitrary(Gen.listOf[T](a.arbitrary).map(Chain.apply))

  def nonEmptyChainOf[T](implicit a: Arbitrary[T]): Gen[Chain[T]] =
    Gen.nonEmptyListOf[T](a.arbitrary).map(Chain.apply)

  it should "not create an additional rule from a non-template rule" in {
    forAll { rule: Rule =>
      val extraParam = ScalastyleRulesRepository.extraParam(rule.key)
      val expected = rule.copy(
        template = false,
        params = Chain(extraParam)
      )
      ScalastyleRulesRepository.fromTemplate(rule.copy(params = Chain.empty)) shouldBe NonEmptyChain.one(
        expected
      )
    }
  }

  it should "not create an additional rule if the instance is blacklisted" in {
    check(
      forAllNoShrink(
        Arbitrary.arbitrary[Rule],
        nonEmptyChainOf[Param],
        Gen.oneOf(ScalastyleRulesRepository.SkipTemplateInstances.toList)
      ) { (rule, params, className) =>
        val newRule = rule.copy(key = className, params = params)
        val extraParam = ScalastyleRulesRepository.extraParam(newRule.key)
        val expected = newRule.copy(
          key = className + "-template",
          template = true,
          params = params :+ extraParam
        )

        ScalastyleRulesRepository.fromTemplate(newRule) === NonEmptyChain.one(expected)
      }
    )
  }

  it should "create an additional rule for templates" in {
    check(
      forAllNoShrink(
        Arbitrary.arbitrary[Rule],
        nonEmptyChainOf[Param]
      ) { (rule, params) =>
        val newRule = rule.copy(params = params)
        val extraParam = ScalastyleRulesRepository.extraParam(newRule.key)
        val instance = newRule.copy(
          template = false,
          params = params :+ extraParam
        )
        val template = instance.copy(
          key = instance.key + "-template",
          template = true
        )

        ScalastyleRulesRepository.fromTemplate(newRule) === NonEmptyChain(template, instance)
      }
    )
  }

  it should "create an additional param with scalastyle class name" in {
    val expected = Param(
      name = "ruleClass",
      typ = ParamType.String,
      description = "Scalastyle's rule (checker) class name.",
      default = "scalastyle.class.name.test"
    )

    ScalastyleRulesRepository.extraParam("scalastyle.class.name.test") shouldBe expected
  }
} 
Example 20
Source File: ScoverageMeasures.scala    From sonar-scala   with GNU Lesser General Public License v3.0 5 votes vote down vote up
package com.mwz.sonar.scala
package scoverage

import scala.jdk.CollectionConverters._

import ScoverageMeasures._
import cats.data.Chain
import cats.data.Chain._
import cats.instances.option._
import cats.instances.string._
import cats.instances.tuple._
import cats.syntax.bitraverse._
import cats.syntax.eq._
import cats.syntax.foldable._
import org.sonar.api.ce.measure.Component
import org.sonar.api.ce.measure.MeasureComputer
import org.sonar.api.ce.measure.MeasureComputer.MeasureComputerContext
import org.sonar.api.ce.measure.MeasureComputer.{MeasureComputerDefinition, MeasureComputerDefinitionContext}

final class ScoverageMeasures extends MeasureComputer {
  override def define(context: MeasureComputerDefinitionContext): MeasureComputerDefinition =
    context
      .newDefinitionBuilder()
      .setInputMetrics(sumMetrics.toList: _*)
      .setOutputMetrics((sumMetrics ++ percentageMetrics.map(_._1)).toList: _*)
      .build()

  override def compute(context: MeasureComputerContext): Unit = {
    if (context.getComponent.getType.name =!= Component.Type.FILE.name) {
      val summed: Map[String, Int] =
        sumMetrics
          .map { metric =>
            (
              metric,
              Chain
                .fromSeq(
                  context
                    .getChildrenMeasures(metric)
                    .asScala
                    .toSeq
                )
                .reduceLeftToOption(_.getIntValue)((acc, measure) => acc + measure.getIntValue)
            )
          }
          .collect { case (key, Some(sum)) => (key, sum) }
          .iterator
          .toMap
      summed.foreach { case (key, sum) => context.addMeasure(key, sum) }

      val percentages: Map[String, BigDecimal] =
        percentageMetrics
          .map {
            case (key, (total, hits)) =>
              (
                key,
                (summed.get(total), summed.get(hits)).bisequence
                  .map {
                    case (total, hits) =>
                      if (total > 0) BigDecimal.valueOf(hits.toLong) / total * 100
                      else BigDecimal(0)
                  }
              )
          }
          .collect { case (key, Some(sum)) => (key, sum) }
          .iterator
          .toMap
      percentages.foreach { case (key, percentage) => context.addMeasure(key, round(percentage)) }
    }
  }
}

object ScoverageMeasures {
  // individual metric -> total metric
  val sumMetrics: Chain[String] = Chain(
    ScoverageMetrics.statements.key,
    ScoverageMetrics.coveredStatements.key,
    ScoverageMetrics.branches.key,
    ScoverageMetrics.coveredBranches.key
  )
  // metric -> (total, hits)
  val percentageMetrics: Chain[(String, (String, String))] =
    Chain(
      (ScoverageMetrics.statementCoverage.key -> (
        (
          ScoverageMetrics.statements.key,
          ScoverageMetrics.coveredStatements.key
        )
      )),
      (ScoverageMetrics.branchCoverage.key -> (
        (
          ScoverageMetrics.branches.key,
          ScoverageMetrics.coveredBranches.key
        )
      ))
    )

  private[scoverage] def round(x: BigDecimal): Double =
    x.setScale(scale = 2, mode = BigDecimal.RoundingMode.HALF_EVEN).toDouble
} 
Example 21
Source File: ScapegoatRules.scala    From sonar-scala   with GNU Lesser General Public License v3.0 5 votes vote down vote up
package com.mwz.sonar.scala.metadata
package scapegoat

import cats.data.Chain
import cats.data.NonEmptyChain

object ScapegoatRules {
  // TODO: Refactor AllInspections to be a NonEmptyChain.
  lazy val rules: NonEmptyChain[Rule] =
    NonEmptyChain.fromChainUnsafe(
      Chain.fromSeq(ScapegoatInspections.AllInspections.map(toRule))
    )

  private[metadata] def toRule(inspection: ScapegoatInspection): Rule = {
    Rule(
      key = inspection.id,
      name = inspection.name.replaceAll("`", ""),
      mdDescription = mdDescription(inspection),
      sonarMdDescription = sonarMdDescription(inspection),
      severity = toSeverity(inspection.defaultLevel),
      template = false,
      params = Chain.empty
    )
  }

  private[metadata] def mdDescription(inspection: ScapegoatInspection): String =
    s"*${inspection.description}*" +
    s"\n\n${inspection.explanation}"

  // SonarQube's markdown parser converts any '=' characters in the middle of a sentence
  // into headings, so we're using a 7th level heading (which doesn't have any style properties)
  // to make it ignore the '=' characters and make it look like regular text.
  // Any inline code blocks between `` are also ignored until Scapegoat has all code blocks wrapped in ``.
  private[metadata] def sonarMdDescription(inspection: ScapegoatInspection): String =
    s"*${inspection.description.replaceAll("`", "")}*" +
    s"\n\n======= ${inspection.explanation.replaceAll("`", "")}"

  private[metadata] def toSeverity(level: Level): Severity =
    level match {
      case Level.Error   => Severity.Major
      case Level.Warning => Severity.Minor
      case Level.Info    => Severity.Info
    }
}