move package

This commit is contained in:
jilen 2024-12-17 19:51:19 +08:00
parent a0ceea91a9
commit 2e7e7df4a3
13 changed files with 227 additions and 54 deletions

View file

@ -0,0 +1,3 @@
package minisql
type QueryMeta

View file

@ -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
}
}

View 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
}
}
}

View 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)
???
}
}

View file

@ -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")

View file

@ -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

View file

@ -63,6 +63,6 @@ object ReifyStatement {
emptySetContainsToken: Token => Token,
liftMap: SMap[String, (Any, Any)]
): (Token) = {
statement
???
}
}

View file

@ -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]

View file

@ -7,7 +7,7 @@ import minisql.ast.{
NumericOperator,
BooleanOperator
}
import minisql.dsl.*
import minisql.*
import scala.quoted._
private[parsing] def operationParsing(

View file

@ -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]] =

View file

@ -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 =>

View file

@ -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"))
)
}
}

View file

@ -0,0 +1 @@