Skip to content

Commit

Permalink
type class helper for parseRs
Browse files Browse the repository at this point in the history
  • Loading branch information
alterationx10 committed Oct 27, 2024
1 parent df66616 commit 2b2169f
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 59 deletions.
58 changes: 21 additions & 37 deletions example/src/main/scala/app/wishingtree/PiggyExample.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import dev.wishingtree.branch.piggy.ResourcePool
import dev.wishingtree.branch.piggy.Sql.ps
import org.postgresql.ds.PGSimpleDataSource

import java.sql.Connection
import java.sql.{Connection, PreparedStatement}
import javax.sql.DataSource
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, ExecutionContext, Future}

import dev.wishingtree.branch.piggy.Sql.*
object PiggyExample {

val ddl =
Expand Down Expand Up @@ -56,42 +56,26 @@ object PiggyExample {

def main(args: Array[String]): Unit = {

// Clear out previous rus
connPool.use(conn => {
val stmt = conn.createStatement()
stmt.execute(ddl)
stmt.execute(s"TRUNCATE TABLE person")
stmt.close()
})

// Insert 100 records sequentially
(1 to 100).foreach { i =>
println(s"Hello from ${i}")
connPool.use[Unit] { conn =>
println(s"Creating person ${i}")
val name = s"Mark-$i"
val age = i
val ins =
ps"INSERT INTO person (name, age) VALUES ($name, $age)" (using conn)
ins.execute()
println(s"Created person ${i}")
}
connPool.use { conn =>
given Connection = conn
val name = "Alterationx10"
val age = 1234
val ins: PreparedStatement =
ps"INSERT INTO person (name, age) VALUES ($name, $age)"
// Turns into INSERT INTO person (name, age) VALUES (?, ?)
// and then provides the values to the PreparedStatement
ins.execute()
val q =
ps"SELECT name, age FROM person where name = $name"
val resultSet = q.executeQuery()
val (dbName, dbAge) =
resultSet.tupledList[(String, Int)].head
// Iterates through the result sets and
// pulls out values based in the type and
// length of the Tuple type argument.
// use tupledList[T <: Tuple] for multiple rows
println(s"Name: $dbName, Age: $dbAge")
}

// Insert 100 records in parallel
val futures = (1 to 100).map { i =>
Future {
println(s"Hello from ${i}")
connPool.use[Unit] { conn =>
println(s"Creating person ${i}")
Person(-1, s"Mark-$i", i).prepareInsert(using conn).execute()
println(s"Created person ${i}")
}
}
}

// Wait for all futures to complete, so we don't terminate early
Await.result(Future.sequence(futures), Duration.Inf)

}
}
95 changes: 81 additions & 14 deletions piggy/src/main/scala/dev/wishingtree/branch/piggy/Sql.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,92 @@ package dev.wishingtree.branch.piggy
import java.sql.{Connection, PreparedStatement, ResultSet}
import scala.compiletime.*

trait ResultSetGetter[A] {
def get(rs: ResultSet)(index: Int): A
}

object ResultSetGetter {

given ResultSetGetter[Int] = new ResultSetGetter[Int] {
def get(rs: ResultSet)(index: Int): Int = rs.getInt(index)
}

given ResultSetGetter[Long] = new ResultSetGetter[Long] {
def get(rs: ResultSet)(index: Int): Long = rs.getLong(index)
}

given ResultSetGetter[Float] = new ResultSetGetter[Float] {
def get(rs: ResultSet)(index: Int): Float = rs.getFloat(index)
}

given ResultSetGetter[Double] = new ResultSetGetter[Double] {
def get(rs: ResultSet)(index: Int): Double = rs.getDouble(index)
}

given ResultSetGetter[String] = new ResultSetGetter[String] {
def get(rs: ResultSet)(index: Int): String = rs.getString(index)
}

given ResultSetGetter[Boolean] = new ResultSetGetter[Boolean] {
def get(rs: ResultSet)(index: Int): Boolean = rs.getBoolean(index)
}

}

trait PreparedStatementSetter[A] {
def set(ps: PreparedStatement)(index: Int)(value: A): Unit
}

object PreparedStatementSetter {

given PreparedStatementSetter[Int] = new PreparedStatementSetter[Int] {
def set(ps: PreparedStatement)(index: Int)(value: Int): Unit =
ps.setInt(index, value)
}

given PreparedStatementSetter[Long] = new PreparedStatementSetter[Long] {
def set(ps: PreparedStatement)(index: Int)(value: Long): Unit =
ps.setLong(index, value)
}

given PreparedStatementSetter[Float] = new PreparedStatementSetter[Float] {
def set(ps: PreparedStatement)(index: Int)(value: Float): Unit =
ps.setFloat(index, value)
}

given PreparedStatementSetter[Double] = new PreparedStatementSetter[Double] {
def set(ps: PreparedStatement)(index: Int)(value: Double): Unit =
ps.setDouble(index, value)
}

given PreparedStatementSetter[String] = new PreparedStatementSetter[String] {
def set(ps: PreparedStatement)(index: Int)(value: String): Unit =
ps.setString(index, value)
}

given PreparedStatementSetter[Boolean] =
new PreparedStatementSetter[Boolean] {
def set(ps: PreparedStatement)(index: Int)(value: Boolean): Unit =
ps.setBoolean(index, value)
}

}

object Sql {

private inline def parseRs[T <: Tuple](rs: ResultSet)(index: Int): Tuple =
inline erasedValue[T] match
case _: EmptyTuple =>
case _: EmptyTuple =>
EmptyTuple
case _: (Int *: ts) =>
rs.getInt(index) *: parseRs[ts](rs)(index + 1)
case _: (Long *: ts) =>
rs.getLong(index) *: parseRs[ts](rs)(index + 1)
case _: (Float *: ts) =>
rs.getFloat(index) *: parseRs[ts](rs)(index + 1)
case _: (Double *: ts) =>
rs.getDouble(index) *: parseRs[ts](rs)(index + 1)
case _: (String *: ts) =>
rs.getString(index) *: parseRs[ts](rs)(index + 1)
case _: (Boolean *: ts) =>
rs.getBoolean(index) *: parseRs[ts](rs)(index + 1)
case _ => error("Unsupported type")
case _: (t *: ts) =>
summonInline[ResultSetGetter[t]].get(rs)(index) *: parseRs[ts](rs)(
index + 1
)

private inline def setPs[A](ps: PreparedStatement)(index: Int)(
value: A
): Unit =
summonInline[PreparedStatementSetter[A]].set(ps)(index)(value)

extension (rs: ResultSet) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,11 @@ class RequestHandlerSpec extends HttpFunSuite {
Response(Map.empty, "Aloha")
}
}

val alohaPf : PF = {
case HttpVerb.GET -> >> / "aloha" => AlohaGreeter()
}

contextFixture.test("RequestHandler") { (server, fn) =>
val testHandler = fn {
case HttpVerb.GET -> >> / "aloha" => AlohaGreeter()

val testHandler = fn { case HttpVerb.GET -> >> / "aloha" =>
AlohaGreeter()
}

ContextHandler.registerHandler(testHandler)(using server)
Expand All @@ -32,7 +28,7 @@ class RequestHandlerSpec extends HttpFunSuite {

val response = client.send(
HttpRequest.newBuilder
.uri(URI.create(s"http://localhost:${server.getAddress.getPort}"))
.uri(URI.create(s"http://localhost:${server.getAddress.getPort}/aloha"))
.build,
HttpResponse.BodyHandlers.ofString
)
Expand Down

0 comments on commit 2b2169f

Please sign in to comment.