move package
This commit is contained in:
parent
a0ceea91a9
commit
2e7e7df4a3
13 changed files with 227 additions and 54 deletions
3
src/main/scala/minisql/Meta.scala
Normal file
3
src/main/scala/minisql/Meta.scala
Normal file
|
@ -0,0 +1,3 @@
|
|||
package minisql
|
||||
|
||||
type QueryMeta
|
|
@ -1,8 +1,23 @@
|
|||
package minisql
|
||||
|
||||
import scala.util.Try
|
||||
|
||||
trait ParamEncoder[E] {
|
||||
|
||||
type Stmt
|
||||
|
||||
def setParam(s: Stmt, idx: Int, v: E): Unit
|
||||
}
|
||||
|
||||
trait ColumnDecoder[X] {
|
||||
|
||||
type DBRow
|
||||
|
||||
def decode(row: DBRow, idx: Int): Try[X]
|
||||
}
|
||||
|
||||
object ColumnDecoder {
|
||||
type Aux[R, X] = ColumnDecoder[X] {
|
||||
type DBRow = R
|
||||
}
|
||||
}
|
||||
|
|
86
src/main/scala/minisql/Quoted.scala
Normal file
86
src/main/scala/minisql/Quoted.scala
Normal file
|
@ -0,0 +1,86 @@
|
|||
package minisql
|
||||
|
||||
import minisql.*
|
||||
import minisql.idiom.*
|
||||
import minisql.parsing.*
|
||||
import minisql.util.*
|
||||
import minisql.ast.{Ast, Entity, Map, Property, Ident, Filter, given}
|
||||
import scala.quoted.*
|
||||
import scala.compiletime.*
|
||||
import scala.compiletime.ops.string.*
|
||||
import scala.collection.immutable.{Map => IMap}
|
||||
|
||||
opaque type Quoted <: Ast = Ast
|
||||
|
||||
opaque type Query[E] <: Quoted = Quoted
|
||||
|
||||
opaque type EntityQuery[E] <: Query[E] = Query[E]
|
||||
|
||||
object EntityQuery {
|
||||
extension [E](inline e: EntityQuery[E]) {
|
||||
inline def map[E1](inline f: E => E1): EntityQuery[E1] = {
|
||||
transform(e)(f)(Map.apply)
|
||||
}
|
||||
|
||||
inline def filter(inline f: E => Boolean): EntityQuery[E] = {
|
||||
transform(e)(f)(Filter.apply)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inline def transform[A, B](inline q1: Quoted)(
|
||||
inline f: A => B
|
||||
)(inline fast: (Ast, Ident, Ast) => Ast): Quoted = {
|
||||
fast(q1, f.param0, f.body)
|
||||
}
|
||||
|
||||
inline def query[E](inline table: String): EntityQuery[E] =
|
||||
Entity(table, Nil)
|
||||
|
||||
extension [A, B](inline f1: A => B) {
|
||||
private inline def param0 = parsing.parseParamAt(f1, 0)
|
||||
private inline def body = parsing.parseBody(f1)
|
||||
}
|
||||
|
||||
extension [A1, A2, B](inline f1: (A1, A2) => B) {
|
||||
private inline def param0 = parsing.parseParamAt(f1, 0)
|
||||
private inline def param1 = parsing.parseParamAt(f1, 1)
|
||||
private inline def body = parsing.parseBody(f1)
|
||||
}
|
||||
|
||||
def lift[X](x: X)(using e: ParamEncoder[X]): X = throw NonQuotedException()
|
||||
|
||||
class NonQuotedException extends Exception("Cannot be used at runtime")
|
||||
|
||||
private[minisql] inline def compile[I <: Idiom, N <: NamingStrategy](
|
||||
inline q: Quoted,
|
||||
inline idiom: I,
|
||||
inline naming: N
|
||||
): Statement = ${ compileImpl[I, N]('q, 'idiom, 'naming) }
|
||||
|
||||
private def compileImpl[I <: Idiom, N <: NamingStrategy](
|
||||
q: Expr[Quoted],
|
||||
idiom: Expr[I],
|
||||
n: Expr[N]
|
||||
)(using Quotes, Type[I], Type[N]): Expr[Statement] = {
|
||||
import quotes.reflect.*
|
||||
q.value match {
|
||||
case Some(ast) =>
|
||||
val idiom = LoadObject[I].getOrElse(
|
||||
report.errorAndAbort(s"Idiom not known at compile")
|
||||
)
|
||||
|
||||
val naming = LoadNaming
|
||||
.static[N]
|
||||
.getOrElse(report.errorAndAbort(s"NamingStrategy not known at compile"))
|
||||
|
||||
val stmt = idiom.translate(ast)(using naming)
|
||||
Expr(stmt._2)
|
||||
case None =>
|
||||
report.info("Dynamic Query")
|
||||
'{
|
||||
$idiom.translate($q)(using $n)._2
|
||||
}
|
||||
|
||||
}
|
||||
}
|
66
src/main/scala/minisql/context/Context.scala
Normal file
66
src/main/scala/minisql/context/Context.scala
Normal file
|
@ -0,0 +1,66 @@
|
|||
package minisql.context
|
||||
|
||||
import scala.deriving.*
|
||||
import scala.compiletime.*
|
||||
import scala.util.Try
|
||||
import minisql.util.*
|
||||
import minisql.idiom.{Idiom, Statement}
|
||||
import minisql.{NamingStrategy, ParamEncoder}
|
||||
import minisql.ColumnDecoder
|
||||
|
||||
trait Context[I <: Idiom, N <: NamingStrategy] { selft =>
|
||||
|
||||
val idiom: I
|
||||
val naming: NamingStrategy
|
||||
|
||||
type DBStatement
|
||||
type DBRow
|
||||
type DBResultSet
|
||||
|
||||
trait RowExtract[A] {
|
||||
def extract(row: DBRow): Try[A]
|
||||
}
|
||||
|
||||
object RowExtract {
|
||||
|
||||
private class ExtractorImpl[A](
|
||||
decoders: IArray[Any],
|
||||
m: Mirror.ProductOf[A]
|
||||
) extends RowExtract[A] {
|
||||
def extract(row: DBRow): Try[A] = {
|
||||
val decodedFields = decoders.zipWithIndex.traverse {
|
||||
case (d, i) =>
|
||||
d.asInstanceOf[Decoder[?]].decode(row, i)
|
||||
}
|
||||
decodedFields.map { vs =>
|
||||
m.fromProduct(Tuple.fromIArray(vs))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline given [P <: Product](using m: Mirror.ProductOf[P]): RowExtract[P] = {
|
||||
val decoders = summonAll[Tuple.Map[m.MirroredElemTypes, Decoder]]
|
||||
ExtractorImpl(decoders.toIArray.asInstanceOf, m)
|
||||
}
|
||||
}
|
||||
|
||||
type Encoder[X] = ParamEncoder[X] {
|
||||
type Stmt = DBStatement
|
||||
}
|
||||
|
||||
type Decoder[X] = ColumnDecoder.Aux[DBRow, X]
|
||||
|
||||
type DBIO[X] = (
|
||||
statement: Statement,
|
||||
params: (Any, Encoder[?]),
|
||||
extract: RowExtract[X]
|
||||
)
|
||||
|
||||
inline def io[E](
|
||||
inline q: minisql.Query[E]
|
||||
)(using r: RowExtract[E]): DBIO[Seq[E]] = {
|
||||
val statement = minisql.compile(q, idiom, naming)
|
||||
???
|
||||
}
|
||||
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package minisql.dsl
|
||||
|
||||
import minisql.*
|
||||
import minisql.parsing.*
|
||||
import minisql.ast.{Ast, Entity, Map, Property, Ident, given}
|
||||
import scala.quoted.*
|
||||
import scala.compiletime.*
|
||||
import scala.compiletime.ops.string.*
|
||||
import scala.collection.immutable.{Map => IMap}
|
||||
|
||||
opaque type Quoted <: Ast = Ast
|
||||
|
||||
opaque type Query[E] <: Quoted = Quoted
|
||||
|
||||
opaque type EntityQuery[E] <: Query[E] = Query[E]
|
||||
|
||||
extension [E](inline e: EntityQuery[E]) {
|
||||
inline def map[E1](inline f: E => E1): EntityQuery[E1] = {
|
||||
transform(e)(f)(Map.apply)
|
||||
}
|
||||
}
|
||||
|
||||
private inline def transform[A, B](inline q1: Quoted)(
|
||||
inline f: A => B
|
||||
)(inline fast: (Ast, Ident, Ast) => Ast): Quoted = {
|
||||
fast(q1, f.param0, f.body)
|
||||
}
|
||||
|
||||
inline def query[E](inline table: String): EntityQuery[E] =
|
||||
Entity(table, Nil)
|
||||
|
||||
extension [A, B](inline f1: A => B) {
|
||||
private inline def param0 = parsing.parseParamAt(f1, 0)
|
||||
private inline def body = parsing.parseBody(f1)
|
||||
}
|
||||
|
||||
extension [A1, A2, B](inline f1: (A1, A2) => B) {
|
||||
private inline def param0 = parsing.parseParamAt(f1, 0)
|
||||
private inline def param1 = parsing.parseParamAt(f1, 1)
|
||||
private inline def body = parsing.parseBody(f1)
|
||||
}
|
||||
|
||||
def lift[X](x: X)(using e: ParamEncoder[X]): X = throw NonQuotedException()
|
||||
|
||||
class NonQuotedException extends Exception("Cannot be used at runtime")
|
|
@ -14,7 +14,7 @@ trait Idiom extends Capabilities {
|
|||
|
||||
def liftingPlaceholder(index: Int): String
|
||||
|
||||
def translate(ast: Ast)(implicit naming: NamingStrategy): (Ast, Statement)
|
||||
def translate(ast: Ast)(using naming: NamingStrategy): (Ast, Statement)
|
||||
|
||||
def format(queryString: String): String = queryString
|
||||
|
||||
|
|
|
@ -63,6 +63,6 @@ object ReifyStatement {
|
|||
emptySetContainsToken: Token => Token,
|
||||
liftMap: SMap[String, (Any, Any)]
|
||||
): (Token) = {
|
||||
statement
|
||||
???
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package minisql.parsing
|
|||
import scala.quoted.*
|
||||
import minisql.ParamEncoder
|
||||
import minisql.ast
|
||||
import minisql.dsl.*
|
||||
import minisql.*
|
||||
|
||||
private[parsing] def liftParsing(
|
||||
astParser: => Parser[ast.Ast]
|
||||
|
|
|
@ -7,7 +7,7 @@ import minisql.ast.{
|
|||
NumericOperator,
|
||||
BooleanOperator
|
||||
}
|
||||
import minisql.dsl.*
|
||||
import minisql.*
|
||||
import scala.quoted._
|
||||
|
||||
private[parsing] def operationParsing(
|
||||
|
|
|
@ -1,6 +1,24 @@
|
|||
package minisql.util
|
||||
|
||||
import scala.util.Try
|
||||
import scala.util.*
|
||||
|
||||
extension [A](xs: IArray[A]) {
|
||||
private[minisql] def traverse[B](f: A => Try[B]): Try[IArray[B]] = {
|
||||
val out = IArray.newBuilder[Any]
|
||||
var left: Option[Throwable] = None
|
||||
xs.foreach { (v) =>
|
||||
if (!left.isDefined) {
|
||||
f(v) match {
|
||||
case Failure(e) =>
|
||||
left = Some(e)
|
||||
case Success(r) =>
|
||||
out += r
|
||||
}
|
||||
}
|
||||
}
|
||||
left.toLeft(out.result().asInstanceOf).toTry
|
||||
}
|
||||
}
|
||||
|
||||
object CollectTry {
|
||||
def apply[T](list: List[Try[T]]): Try[List[T]] =
|
||||
|
|
|
@ -5,10 +5,15 @@ import scala.util.Try
|
|||
|
||||
object LoadObject {
|
||||
|
||||
def apply[T](using Quotes, Type[T]): Try[T] = {
|
||||
import quotes.reflect.*
|
||||
apply(TypeRepr.of[T])
|
||||
}
|
||||
|
||||
def apply[T](using Quotes)(ot: quotes.reflect.TypeRepr): Try[T] = Try {
|
||||
import quotes.reflect.*
|
||||
val moduleClsName = ot.typeSymbol.companionModule.moduleClass.fullName
|
||||
val moduleCls = Class.forName(moduleClsName)
|
||||
val moduleCls = Class.forName(moduleClsName)
|
||||
val field = moduleCls
|
||||
.getFields()
|
||||
.find { f =>
|
||||
|
|
|
@ -4,11 +4,35 @@ import minisql.ast.*
|
|||
|
||||
class ParsingSuite extends munit.FunSuite {
|
||||
|
||||
inline def testParseInline(inline x: Any, ast: Ast) = {
|
||||
test("Ident") {
|
||||
val x = 1
|
||||
assertEquals(Parsing.parse(x), Ident("x"))
|
||||
}
|
||||
|
||||
test("Ident") {
|
||||
val x = 1
|
||||
test("NumericOperator.+") {
|
||||
val a = 1
|
||||
val b = 2
|
||||
assertEquals(
|
||||
Parsing.parse(a + b),
|
||||
BinaryOperation(Ident("a"), NumericOperator.+, Ident("b"))
|
||||
)
|
||||
}
|
||||
|
||||
test("NumericOperator.-") {
|
||||
val a = 1
|
||||
val b = 2
|
||||
assertEquals(
|
||||
Parsing.parse(a - b),
|
||||
BinaryOperation(Ident("a"), NumericOperator.-, Ident("b"))
|
||||
)
|
||||
}
|
||||
|
||||
test("NumericOperator.*") {
|
||||
val a = 1
|
||||
val b = 2
|
||||
assertEquals(
|
||||
Parsing.parse(a * b),
|
||||
BinaryOperation(Ident("a"), NumericOperator.*, Ident("b"))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
1
src/test/scala/minisql/parsing/QuerySuite.scala
Normal file
1
src/test/scala/minisql/parsing/QuerySuite.scala
Normal file
|
@ -0,0 +1 @@
|
|||
|
Loading…
Reference in a new issue