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
|
package minisql
|
||||||
|
|
||||||
|
import scala.util.Try
|
||||||
|
|
||||||
trait ParamEncoder[E] {
|
trait ParamEncoder[E] {
|
||||||
|
|
||||||
type Stmt
|
type Stmt
|
||||||
|
|
||||||
def setParam(s: Stmt, idx: Int, v: E): Unit
|
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 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
|
def format(queryString: String): String = queryString
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,6 @@ object ReifyStatement {
|
||||||
emptySetContainsToken: Token => Token,
|
emptySetContainsToken: Token => Token,
|
||||||
liftMap: SMap[String, (Any, Any)]
|
liftMap: SMap[String, (Any, Any)]
|
||||||
): (Token) = {
|
): (Token) = {
|
||||||
statement
|
???
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package minisql.parsing
|
||||||
import scala.quoted.*
|
import scala.quoted.*
|
||||||
import minisql.ParamEncoder
|
import minisql.ParamEncoder
|
||||||
import minisql.ast
|
import minisql.ast
|
||||||
import minisql.dsl.*
|
import minisql.*
|
||||||
|
|
||||||
private[parsing] def liftParsing(
|
private[parsing] def liftParsing(
|
||||||
astParser: => Parser[ast.Ast]
|
astParser: => Parser[ast.Ast]
|
||||||
|
|
|
@ -7,7 +7,7 @@ import minisql.ast.{
|
||||||
NumericOperator,
|
NumericOperator,
|
||||||
BooleanOperator
|
BooleanOperator
|
||||||
}
|
}
|
||||||
import minisql.dsl.*
|
import minisql.*
|
||||||
import scala.quoted._
|
import scala.quoted._
|
||||||
|
|
||||||
private[parsing] def operationParsing(
|
private[parsing] def operationParsing(
|
||||||
|
|
|
@ -1,6 +1,24 @@
|
||||||
package minisql.util
|
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 {
|
object CollectTry {
|
||||||
def apply[T](list: List[Try[T]]): Try[List[T]] =
|
def apply[T](list: List[Try[T]]): Try[List[T]] =
|
||||||
|
|
|
@ -5,6 +5,11 @@ import scala.util.Try
|
||||||
|
|
||||||
object LoadObject {
|
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 {
|
def apply[T](using Quotes)(ot: quotes.reflect.TypeRepr): Try[T] = Try {
|
||||||
import quotes.reflect.*
|
import quotes.reflect.*
|
||||||
val moduleClsName = ot.typeSymbol.companionModule.moduleClass.fullName
|
val moduleClsName = ot.typeSymbol.companionModule.moduleClass.fullName
|
||||||
|
|
|
@ -4,11 +4,35 @@ import minisql.ast.*
|
||||||
|
|
||||||
class ParsingSuite extends munit.FunSuite {
|
class ParsingSuite extends munit.FunSuite {
|
||||||
|
|
||||||
inline def testParseInline(inline x: Any, ast: Ast) = {
|
test("Ident") {
|
||||||
|
val x = 1
|
||||||
assertEquals(Parsing.parse(x), Ident("x"))
|
assertEquals(Parsing.parse(x), Ident("x"))
|
||||||
}
|
}
|
||||||
|
|
||||||
test("Ident") {
|
test("NumericOperator.+") {
|
||||||
val x = 1
|
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