Initial version -- ~45 minutes of poking around.
This commit is contained in:
commit
439c28c1c7
13 changed files with 310 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
target/
|
||||
project/target/
|
||||
project/project/
|
||||
.version
|
||||
16
.pre-commit-config.yaml
Normal file
16
.pre-commit-config.yaml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.5.0
|
||||
hooks:
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- id: fix-byte-order-marker
|
||||
- id: mixed-line-ending
|
||||
args: ['--fix=lf']
|
||||
description: Enforces using only 'LF' line endings.
|
||||
- id: trailing-whitespace
|
||||
- repo: https://git.garrity.co/garrity-software/gs-pre-commit-scala
|
||||
rev: v1.0.1
|
||||
hooks:
|
||||
- id: scalafmt
|
||||
72
.scalafmt.conf
Normal file
72
.scalafmt.conf
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
// See: https://github.com/scalameta/scalafmt/tags for the latest tags.
|
||||
version = 3.8.1
|
||||
runner.dialect = scala3
|
||||
maxColumn = 80
|
||||
|
||||
rewrite {
|
||||
rules = [RedundantBraces, RedundantParens, Imports, SortModifiers]
|
||||
imports.expand = true
|
||||
imports.sort = scalastyle
|
||||
redundantBraces.ifElseExpressions = true
|
||||
redundantBraces.stringInterpolation = true
|
||||
}
|
||||
|
||||
indent {
|
||||
main = 2
|
||||
callSite = 2
|
||||
defnSite = 2
|
||||
extendSite = 4
|
||||
withSiteRelativeToExtends = 2
|
||||
commaSiteRelativeToExtends = 2
|
||||
}
|
||||
|
||||
align {
|
||||
preset = more
|
||||
openParenCallSite = false
|
||||
openParenDefnSite = false
|
||||
}
|
||||
|
||||
newlines {
|
||||
implicitParamListModifierForce = [before,after]
|
||||
topLevelStatementBlankLines = [
|
||||
{
|
||||
blanks = 1
|
||||
}
|
||||
]
|
||||
afterCurlyLambdaParams = squash
|
||||
}
|
||||
|
||||
danglingParentheses {
|
||||
defnSite = true
|
||||
callSite = true
|
||||
ctrlSite = true
|
||||
exclude = []
|
||||
}
|
||||
|
||||
verticalMultiline {
|
||||
atDefnSite = true
|
||||
arityThreshold = 2
|
||||
newlineAfterOpenParen = true
|
||||
}
|
||||
|
||||
comments {
|
||||
wrap = standalone
|
||||
}
|
||||
|
||||
docstrings {
|
||||
style = "SpaceAsterisk"
|
||||
oneline = unfold
|
||||
wrap = yes
|
||||
forceBlankLineBefore = true
|
||||
}
|
||||
|
||||
project {
|
||||
excludePaths = [
|
||||
"glob:**target/**",
|
||||
"glob:**.metals/**",
|
||||
"glob:**.bloop/**",
|
||||
"glob:**.bsp/**",
|
||||
"glob:**metals.sbt",
|
||||
"glob:**.git/**"
|
||||
]
|
||||
}
|
||||
3
README.md
Normal file
3
README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# fizzbuzz
|
||||
|
||||
This project is an over-engineered fizzbuzz to buzz your fizz.
|
||||
59
build.sbt
Normal file
59
build.sbt
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
val scala3: String = "3.8.4"
|
||||
|
||||
ThisBuild / scalaVersion := scala3
|
||||
ThisBuild / gsProjectName := "fizzbuzz"
|
||||
|
||||
ThisBuild / externalResolvers := Seq(
|
||||
"Garrity Software Mirror" at "https://maven.garrity.co/releases",
|
||||
"Garrity Software Releases" at "https://maven.garrity.co/gs"
|
||||
)
|
||||
|
||||
lazy val sharedSettings = Seq(
|
||||
scalaVersion := scala3,
|
||||
version := calVer.value,
|
||||
publish / skip := true,
|
||||
publishLocal / skip := true,
|
||||
publishArtifact := false
|
||||
)
|
||||
|
||||
val Deps = new {
|
||||
val Cats = new {
|
||||
val Core: ModuleID = "org.typelevel" %% "cats-core" % "2.13.0"
|
||||
val Effect: ModuleID = "org.typelevel" %% "cats-effect" % "3.7.0"
|
||||
}
|
||||
|
||||
val Fs2 = new {
|
||||
private val Version: String = "3.13.0"
|
||||
|
||||
val Core: ModuleID = "co.fs2" %% "fs2-core" % Version
|
||||
val IO: ModuleID = "co.fs2" %% "fs2-io" % Version
|
||||
}
|
||||
|
||||
val Gs = new {
|
||||
val Std: ModuleID = "gs" %% "gs-std-core-v0" % "0.1.3"
|
||||
val Datagen: ModuleID = "gs" %% "gs-datagen-core-v0" % "0.4.1"
|
||||
}
|
||||
|
||||
val MUnit: ModuleID = "org.scalameta" %% "munit" % "1.3.0"
|
||||
}
|
||||
|
||||
lazy val testSettings = Seq(
|
||||
libraryDependencies ++= Seq(
|
||||
Deps.MUnit % Test,
|
||||
Deps.Gs.Datagen % Test
|
||||
)
|
||||
)
|
||||
|
||||
lazy val fizzbuzz = project
|
||||
.in(file("."))
|
||||
.settings(sharedSettings)
|
||||
.settings(testSettings)
|
||||
.settings(name := s"${gsProjectName.value}")
|
||||
.settings(fork := true)
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
Deps.Cats.Core,
|
||||
Deps.Cats.Effect,
|
||||
Deps.Fs2.Core
|
||||
)
|
||||
)
|
||||
1
project/build.properties
Normal file
1
project/build.properties
Normal file
|
|
@ -0,0 +1 @@
|
|||
sbt.version=1.12.11
|
||||
33
project/plugins.sbt
Normal file
33
project/plugins.sbt
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
def selectCredentials(): Credentials =
|
||||
if ((Path.userHome / ".sbt" / ".credentials").exists())
|
||||
Credentials(Path.userHome / ".sbt" / ".credentials")
|
||||
else
|
||||
Credentials.apply(
|
||||
realm = "Reposilite",
|
||||
host = "maven.garrity.co",
|
||||
userName = sys.env
|
||||
.get("GS_MAVEN_USER")
|
||||
.getOrElse(
|
||||
throw new RuntimeException(
|
||||
"You must either provide ~/.sbt/.credentials or specify the GS_MAVEN_USER environment variable."
|
||||
)
|
||||
),
|
||||
passwd = sys.env
|
||||
.get("GS_MAVEN_TOKEN")
|
||||
.getOrElse(
|
||||
throw new RuntimeException(
|
||||
"You must either provide ~/.sbt/.credentials or specify the GS_MAVEN_TOKEN environment variable."
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
credentials += selectCredentials()
|
||||
|
||||
externalResolvers := Seq(
|
||||
"Garrity Software Mirror" at "https://maven.garrity.co/releases",
|
||||
"Garrity Software Releases" at "https://maven.garrity.co/gs"
|
||||
)
|
||||
|
||||
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.4.4")
|
||||
addSbtPlugin("gs" % "sbt-garrity-software" % "0.7.0")
|
||||
addSbtPlugin("gs" % "sbt-gs-calver" % "0.2.0")
|
||||
18
src/main/scala/gs/fizzbuzz/Condition.scala
Normal file
18
src/main/scala/gs/fizzbuzz/Condition.scala
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package gs.fizzbuzz
|
||||
|
||||
import cats.Applicative
|
||||
|
||||
trait Condition[F[_], Input, Output]:
|
||||
def matches(input: Input): F[Boolean]
|
||||
def output: Output
|
||||
end Condition
|
||||
|
||||
object Condition:
|
||||
|
||||
final class Noop[F[_]: Applicative, Input] extends Condition[F, Input, Nothing]:
|
||||
override def matches(input: Input): F[Boolean] =
|
||||
Applicative[F].pure(false)
|
||||
override def output: Nothing =
|
||||
throw new IllegalStateException("No-op condition cannot produce output.")
|
||||
|
||||
end Condition
|
||||
15
src/main/scala/gs/fizzbuzz/DefaultCondition.scala
Normal file
15
src/main/scala/gs/fizzbuzz/DefaultCondition.scala
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package gs.fizzbuzz
|
||||
|
||||
import cats.Applicative
|
||||
import cats.Monoid
|
||||
|
||||
final class DefaultCondition[F[_]: Applicative, Input, Out](
|
||||
val comparison: Input,
|
||||
val output: Out,
|
||||
val operator: Monoid[Input]
|
||||
)(using CanEqual[Input, Input]) extends Condition[F, Input, Out]:
|
||||
|
||||
override def matches(input: Input): F[Boolean] =
|
||||
Applicative[F].pure(
|
||||
operator.combine(input, comparison) == operator.empty
|
||||
)
|
||||
26
src/main/scala/gs/fizzbuzz/DefaultFizzBuzz.scala
Normal file
26
src/main/scala/gs/fizzbuzz/DefaultFizzBuzz.scala
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package gs.fizzbuzz
|
||||
|
||||
import cats.syntax.all.*
|
||||
import fs2.Pipe
|
||||
import cats.FlatMap
|
||||
|
||||
class DefaultFizzBuzz[F[_]: FlatMap, Input, Out](
|
||||
val fizzCondition: Condition[F, Input, Out],
|
||||
val buzzCondition: Condition[F, Input, Out]
|
||||
) extends FizzBuzz[F, Input, Out]:
|
||||
def evaluate(input: Input): F[Result[Out]] =
|
||||
for
|
||||
fizz <- fizzCondition.matches(input)
|
||||
buzz <- buzzCondition.matches(input)
|
||||
yield
|
||||
if fizz && buzz then
|
||||
Result.FizzBuzz(fizzCondition.output, buzzCondition.output)
|
||||
else if fizz then
|
||||
Result.Fizz(fizzCondition.output)
|
||||
else if buzz then
|
||||
Result.Buzz(buzzCondition.output)
|
||||
else
|
||||
Result.Unmatched
|
||||
|
||||
def pipe(): Pipe[F, Input, Result[Out]] = in => in.evalMap(evaluate)
|
||||
end DefaultFizzBuzz
|
||||
8
src/main/scala/gs/fizzbuzz/FizzBuzz.scala
Normal file
8
src/main/scala/gs/fizzbuzz/FizzBuzz.scala
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package gs.fizzbuzz
|
||||
|
||||
import fs2.Pipe
|
||||
|
||||
trait FizzBuzz[F[_], Input, Out]:
|
||||
def evaluate(input: Input): F[Result[Out]]
|
||||
def pipe(): Pipe[F, Input, Result[Out]]
|
||||
end FizzBuzz
|
||||
42
src/main/scala/gs/fizzbuzz/Main.scala
Normal file
42
src/main/scala/gs/fizzbuzz/Main.scala
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
package gs.fizzbuzz
|
||||
|
||||
import cats.Monoid
|
||||
import cats.effect.IO
|
||||
import cats.effect.IOApp
|
||||
import cats.effect.ExitCode
|
||||
|
||||
object Main extends IOApp:
|
||||
override def run(args: List[String]): IO[ExitCode] =
|
||||
val op: Monoid[Long] = new Monoid[Long] {
|
||||
override def empty: Long = 0L
|
||||
override def combine(x: Long, y: Long): Long = x % y
|
||||
}
|
||||
|
||||
val fizzCond: Condition[IO, Long, String] = new DefaultCondition(
|
||||
comparison = 3L,
|
||||
output = "Fizz",
|
||||
operator = op
|
||||
)
|
||||
|
||||
val buzzCond: Condition[IO, Long, String] = new DefaultCondition(
|
||||
comparison = 5L,
|
||||
output = "Buzz",
|
||||
operator = op
|
||||
)
|
||||
|
||||
val fb: FizzBuzz[IO, Long, String] = new DefaultFizzBuzz[IO, Long, String](
|
||||
fizzCondition = fizzCond,
|
||||
buzzCondition = buzzCond
|
||||
)
|
||||
|
||||
for
|
||||
ex1 <- fb.evaluate(15)
|
||||
ex2 <- fb.evaluate(9)
|
||||
ex3 <- fb.evaluate(20)
|
||||
ex4 <- fb.evaluate(13)
|
||||
_ <- IO.println(ex1)
|
||||
_ <- IO.println(ex2)
|
||||
_ <- IO.println(ex3)
|
||||
_ <- IO.println(ex4)
|
||||
yield
|
||||
ExitCode.Success
|
||||
13
src/main/scala/gs/fizzbuzz/Result.scala
Normal file
13
src/main/scala/gs/fizzbuzz/Result.scala
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package gs.fizzbuzz
|
||||
|
||||
sealed trait Result[+Out]
|
||||
|
||||
object Result:
|
||||
|
||||
case class Fizz[Out](fizz: Out) extends Result[Out]
|
||||
case class Buzz[Out](buzz: Out) extends Result[Out]
|
||||
case class FizzBuzz[Out](fizz: Out, buzz: Out) extends Result[Out]
|
||||
case object Unmatched extends Result[Nothing]:
|
||||
override def toString(): String = "unmatched"
|
||||
|
||||
end Result
|
||||
Loading…
Add table
Reference in a new issue