forked from lowmelvin/formify-scala
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit f25eb1e
Showing
17 changed files
with
418 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
/project/project/ | ||
/**/target/ | ||
.bsp/ | ||
.idea/ | ||
.bloop/ | ||
.metals/ | ||
.vscode/ |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
rules = [ | ||
DisableSyntax, | ||
# RemoveUnused, | ||
OrganizeImports, | ||
NoValInForComprehension, | ||
] | ||
DisableSyntax.noFinalize = true | ||
DisableSyntax.noIsInstanceOf = true | ||
DisableSyntax.noReturns = true | ||
|
||
// `rules` on compilation | ||
triggered.rules = [ | ||
DisableSyntax | ||
] | ||
|
||
OrganizeImports { | ||
coalesceToWildcardImportThreshold = 6 | ||
expandRelative = true | ||
groups = [ | ||
"cats", | ||
"fs2", | ||
"io", | ||
"org", | ||
"com", | ||
"java." | ||
"javax." | ||
"scala." | ||
"*" | ||
] | ||
groupedImports = AggressiveMerge | ||
removeUnused = false # added for Scala 3 | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
version=3.7.3 | ||
|
||
align.preset = more | ||
maxColumn = 100 | ||
assumeStandardLibraryStripMargin = true | ||
indent.defnSite = 2 | ||
indentOperator.topLevelOnly = false | ||
align.preset = more | ||
align.openParenCallSite = false | ||
newlines.source = keep | ||
newlines.beforeMultiline = keep | ||
newlines.afterCurlyLambdaParams = keep | ||
newlines.alwaysBeforeElseAfterCurlyIf = true | ||
|
||
runner.dialect = scala3 | ||
|
||
rewrite.rules = [ | ||
RedundantBraces | ||
RedundantParens | ||
SortModifiers | ||
] | ||
|
||
rewrite.redundantBraces { | ||
ifElseExpressions = true | ||
includeUnitMethods = false | ||
stringInterpolation = true | ||
} | ||
|
||
rewrite.sortModifiers.order = [ | ||
"private", "final", "override", "protected", | ||
"implicit", "sealed", "abstract", "lazy" | ||
] | ||
|
||
project.excludeFilters = [ | ||
".bloop" | ||
".metals" | ||
".vscode" | ||
".scala-build" | ||
"examples" # Scala 3 scripts and using directives not supported yet | ||
"out" | ||
"scala-version.scala" | ||
] |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
FROM sbtscala/scala-sbt:graalvm-ce-22.3.0-b2-java17_1.9.2_3.3.0 as sbt-graalvm | ||
WORKDIR /app | ||
COPY . . | ||
|
||
ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 | ||
RUN sbt assembly | ||
|
||
RUN gu install native-image | ||
RUN native-image \ | ||
--no-fallback \ | ||
--enable-http \ | ||
--enable-https \ | ||
--static \ | ||
-jar target/**/formify.jar | ||
|
||
FROM scratch | ||
COPY --from=sbt-graalvm /app/formify /app/formify | ||
ENTRYPOINT ["/app/formify"] |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
val CatsVersion = "2.9.0" | ||
val CatsEffectVersion = "3.5.1" | ||
val CatsEffectTestKitVersion = "3.5.1" | ||
val Fs2Version = "3.7.0" | ||
val Log4CatsVersion = "2.6.0" | ||
val MunitVersion = "0.7.29" | ||
val MunitCatsEffectVersion = "1.0.7" | ||
val WeaverCatsVersion = "0.8.3" | ||
val LogbackVersion = "1.4.8" | ||
|
||
ThisBuild / organization := "com.melvinlow" | ||
ThisBuild / scalaVersion := "3.3.0" | ||
ThisBuild / semanticdbEnabled := true | ||
ThisBuild / semanticdbVersion := scalafixSemanticdb.revision | ||
|
||
lazy val root = (project in file(".")) | ||
.settings( | ||
name := "formify", | ||
version := "0.1.0-SNAPSHOT", | ||
libraryDependencies ++= Seq( | ||
"org.typelevel" %% "cats-core" % CatsVersion, | ||
"org.typelevel" %% "cats-effect" % CatsEffectVersion, | ||
"co.fs2" %% "fs2-core" % Fs2Version, | ||
"org.typelevel" %% "log4cats-slf4j" % Log4CatsVersion, | ||
"ch.qos.logback" % "logback-classic" % LogbackVersion % Runtime, | ||
"org.scalameta" %% "munit" % MunitVersion % Test, | ||
"com.disneystreaming" %% "weaver-cats" % WeaverCatsVersion % Test, | ||
"org.typelevel" %% "munit-cats-effect-3" % MunitCatsEffectVersion % Test, | ||
"org.typelevel" %% "cats-effect-testkit" % CatsEffectTestKitVersion % Test | ||
), | ||
scalacOptions ++= Seq( | ||
"-encoding", | ||
"UTF-8", | ||
"-feature", | ||
"-unchecked", | ||
"-deprecation", | ||
"-Wunused:all", | ||
"-Werror", | ||
"-Wvalue-discard", | ||
"-no-indent", | ||
"-explain" | ||
), | ||
testFrameworks ++= List( | ||
new TestFramework("munit.Framework"), | ||
new TestFramework("weaver.framework.CatsEffect") | ||
), | ||
assembly / assemblyJarName := "formify.jar", | ||
assemblyMergeStrategy := { | ||
case PathList("META-INF", "MANIFEST.MF") => MergeStrategy.discard | ||
case x if x.endsWith("module-info.class") => MergeStrategy.discard | ||
case x => assemblyMergeStrategy.value(x) | ||
}, | ||
) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
sbt.version=1.9.2 |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// DO NOT EDIT! This file is auto-generated. | ||
|
||
// This file enables sbt-bloop to create bloop config files. | ||
|
||
addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.8") | ||
|
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.1.1") | ||
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.0") | ||
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.11.0") | ||
addSbtPlugin("com.github.sbt" % "sbt-jacoco" % "3.4.0") | ||
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.6.4") |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<configuration> | ||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> | ||
<withJansi>true</withJansi> | ||
<encoder> | ||
<pattern>[%thread] %highlight(%-5level) %cyan(%logger{15}) - %msg %X %n</pattern> | ||
</encoder> | ||
</appender> | ||
|
||
<!-- <logger name="org.typelevel.slf4j" level="TRACE"/> --> | ||
|
||
<root level="TRACE"> | ||
<appender-ref ref="STDOUT" /> | ||
</root> | ||
</configuration> |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.melvinlow.formify | ||
|
||
import cats.data.* | ||
|
||
import java.net.URLEncoder | ||
|
||
opaque type FormData = Chain[(FormKey, FormValue)] | ||
|
||
object FormData { | ||
inline def Empty: FormData = Chain.empty | ||
|
||
inline def one(key: FormKey, value: FormValue): FormData = Chain.one((key, value)) | ||
|
||
extension (data: FormData) { | ||
inline def underlying: Chain[(FormKey, FormValue)] = data | ||
|
||
inline def ++(other: FormData): FormData = data ++ other | ||
|
||
def prepend(fragment: FormKeyFragment): FormData = | ||
data.map((key, value) => (key.prepend(fragment), value)) | ||
|
||
def compile(using FormKeyCompiler): Chain[(String, String)] = | ||
data.collect { case (k, FormValue(v)) => (k.compile, v) } | ||
|
||
def serialize(using FormKeyCompiler): String = | ||
data.compile.map { (k, v) => | ||
val kenc = URLEncoder.encode(k, "UTF-8") | ||
val venc = URLEncoder.encode(v, "UTF-8") | ||
s"$kenc=$venc" | ||
}.toList.mkString("&") | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
src/main/scala/com/melvinlow/formify/FormDataEncoder.scala
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package com.melvinlow.formify | ||
|
||
import scala.compiletime.* | ||
import scala.compiletime.ops.any.* | ||
import scala.deriving.* | ||
|
||
trait FormDataEncoder[T] { | ||
def encode(data: T): FormData | ||
|
||
extension (data: T) { | ||
inline def asFormData: FormData = encode(data) | ||
} | ||
} | ||
|
||
object FormDataEncoder { | ||
inline def apply[T](using enc: FormDataEncoder[T]) = enc | ||
|
||
inline def encode[T: FormDataEncoder](data: T): FormData = data.asFormData | ||
|
||
inline private def summonEncoders[T, Elems <: Tuple]: List[?] = | ||
inline erasedValue[Elems] match { | ||
case _: EmptyTuple => Nil | ||
case _: (head *: tail) => summonElemEncoder[T, head] :: summonEncoders[T, tail] | ||
} | ||
|
||
inline private def summonElemEncoder[T, Elem]: FormDataEncoder[Elem] | FormValueEncoder[Elem] = | ||
summonFrom { | ||
case formValueEncoder: FormValueEncoder[Elem] => formValueEncoder // prioritize value encoders | ||
case formEncoder: FormDataEncoder[Elem] => formEncoder | ||
case _: Mirror.ProductOf[Elem] => deriveElemEncoder[T, Elem] | ||
} | ||
|
||
inline private def deriveElemEncoder[T, Elem: Mirror.ProductOf]: FormDataEncoder[Elem] = | ||
inline erasedValue[Elem] match { | ||
case _: T => error("infinite recursion derivation") | ||
case _ => derived[Elem] | ||
} | ||
|
||
inline private def summonLabels[Labels <: Tuple]: List[String] = | ||
inline erasedValue[Labels] match { | ||
case _: EmptyTuple => Nil | ||
case _: (head *: tail) => constValue[ToString[head]] :: summonLabels[tail] | ||
} | ||
|
||
inline def derived[T](using m: Mirror.ProductOf[T]): FormDataEncoder[T] = new FormDataEncoder[T] { | ||
lazy val labels = summonLabels[m.MirroredElemLabels] | ||
lazy val encoders = summonEncoders[T, m.MirroredElemTypes] | ||
|
||
override def encode(data: T): FormData = { | ||
val values = data.asInstanceOf[Product].productIterator.toList | ||
|
||
labels.lazyZip(values).lazyZip(encoders).map { | ||
case (label, value, formDataEncoder: FormDataEncoder[v]) => | ||
formDataEncoder.encode(value.asInstanceOf[v]).prepend(FormKeyFragment(label)) | ||
|
||
case (label, value, formValueEncoder: FormValueEncoder[v]) => | ||
FormData.one( | ||
FormKey.one(FormKeyFragment(label)), | ||
formValueEncoder.encode(value.asInstanceOf[v]) | ||
) | ||
}.foldLeft(FormData.Empty)(_ ++ _) | ||
} | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.melvinlow.formify | ||
|
||
import cats.data.* | ||
|
||
opaque type FormKey = NonEmptyChain[FormKeyFragment] | ||
|
||
object FormKey { | ||
inline def one(fragment: FormKeyFragment): FormKey = NonEmptyChain.one(fragment) | ||
|
||
extension (key: FormKey) { | ||
inline def underlying: NonEmptyChain[FormKeyFragment] = key | ||
|
||
inline def prepend(fragment: FormKeyFragment): FormKey = fragment +: key | ||
|
||
inline def compile(using compiler: FormKeyCompiler): String = compiler.compile(key) | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
src/main/scala/com/melvinlow/formify/FormKeyCompiler.scala
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.melvinlow.formify | ||
|
||
import cats.data.* | ||
|
||
trait FormKeyCompiler { | ||
def compile(key: FormKey): String | ||
} | ||
|
||
object FormKeyCompiler { | ||
def make(fn: (NonEmptyChain[String]) => String): FormKeyCompiler = new FormKeyCompiler { | ||
override def compile(key: FormKey): String = fn(key.underlying.map(_.underlying)) | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
src/main/scala/com/melvinlow/formify/FormKeyFragment.scala
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.melvinlow.formify | ||
|
||
opaque type FormKeyFragment = String | ||
|
||
object FormKeyFragment { | ||
inline def apply(fragment: String): FormKeyFragment = fragment | ||
|
||
extension (fragment: FormKeyFragment) { | ||
inline def underlying: String = fragment | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.melvinlow.formify | ||
|
||
opaque type FormValue = Option[String] | ||
|
||
object FormValue { | ||
inline def Empty: FormValue = None | ||
|
||
inline def apply(value: String): FormValue = Some(value) | ||
|
||
inline def unapply(value: FormValue): Option[String] = value | ||
|
||
extension (value: FormValue) { | ||
inline def underlying: Option[String] = value | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
src/main/scala/com/melvinlow/formify/FormValueEncoder.scala
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.melvinlow.formify | ||
|
||
import cats.Contravariant | ||
|
||
trait FormValueEncoder[T] { | ||
def encode(value: T): FormValue | ||
} | ||
|
||
object FormValueEncoder { | ||
inline def apply[T](using enc: FormValueEncoder[T]) = enc | ||
|
||
given Contravariant[FormValueEncoder] with { | ||
def contramap[A, B](fa: FormValueEncoder[A])(f: B => A): FormValueEncoder[B] = | ||
(value: B) => fa.encode(f(value)) | ||
} | ||
} |
Oops, something went wrong.