提交 52e554b9 编写于 作者: 梦境迷离's avatar 梦境迷离

upgrade scalafmt

上级 e6b802b0
version = "2.7.5" version = "3.5.3"
runner.dialect = scala213
maxColumn = 120 maxColumn = 120
align = most align.preset = more
continuationIndent.defnSite = 2
assumeStandardLibraryStripMargin = true
docstrings = JavaDoc
lineEndings = preserve lineEndings = preserve
includeCurlyBraceInSelectChains = false align.stripMargin = false
danglingParentheses = true docstrings.style = AsteriskSpace
docstrings.oneline = keep
continuationIndent.defnSite = 2
danglingParentheses.preset = true
spaces { spaces {
inImportCurlyBraces = true inImportCurlyBraces = true
} }
indentOperator.exemptScope = aloneArgOrBody
includeCurlyBraceInSelectChains = false
align.openParenDefnSite = false
optIn.annotationNewlines = true optIn.annotationNewlines = true
rewrite.rules = [SortImports, RedundantBraces] rewrite.rules = [SortImports, RedundantBraces]
align.openParenDefnSite = false
rewriteTokens = { rewriteTokens = {
"⇒": "=>" "⇒": "=>"
"→": "->" "→": "->"
......
...@@ -8,35 +8,35 @@ ThisBuild / resolvers ++= Seq( ...@@ -8,35 +8,35 @@ ThisBuild / resolvers ++= Seq(
"New snapshots" at "https://s01.oss.sonatype.org/content/repositories/snapshots/" "New snapshots" at "https://s01.oss.sonatype.org/content/repositories/snapshots/"
) )
lazy val scala212 = "2.12.14" lazy val scala212 = "2.12.14"
lazy val scala211 = "2.11.12" lazy val scala211 = "2.11.12"
lazy val scala213 = "2.13.8" lazy val scala213 = "2.13.8"
lazy val lastVersionForExamples = "0.5.2" lazy val lastVersionForExamples = "0.5.2"
lazy val scalatestVersion = "3.2.12" lazy val scalatestVersion = "3.2.12"
lazy val zioVersion = "1.0.14" lazy val zioVersion = "1.0.14"
lazy val zioLoggingVersion = "0.5.14" lazy val zioLoggingVersion = "0.5.14"
lazy val configVersion = "1.4.2" lazy val configVersion = "1.4.2"
lazy val caffeineVersion = "2.9.3" lazy val caffeineVersion = "2.9.3"
lazy val zioRedisVersion = "0.0.0+381-86c20614-SNAPSHOT" // 实验性质的 lazy val zioRedisVersion = "0.0.0+381-86c20614-SNAPSHOT" // 实验性质的
lazy val zioSchemaVersion = "0.1.9" lazy val zioSchemaVersion = "0.1.9"
lazy val scalaLoggingVersion = "3.9.4" lazy val scalaLoggingVersion = "3.9.4"
lazy val playJsonVersion = "2.7.4" lazy val playJsonVersion = "2.7.4"
lazy val log4jVersion = "2.17.2" lazy val log4jVersion = "2.17.2"
lazy val jacksonScalaVersion = "2.13.2" lazy val jacksonScalaVersion = "2.13.2"
lazy val jraftVersion = "1.3.9" lazy val jraftVersion = "1.3.9"
lazy val protocVersion = "3.20.1" lazy val protocVersion = "3.20.1"
lazy val commonSettings = lazy val commonSettings =
Seq( Seq(
organization := "org.bitlap", organization := "org.bitlap",
organizationName := "bitlap", organizationName := "bitlap",
startYear := Some(2022), startYear := Some(2022),
scalaVersion := scala213, scalaVersion := scala213,
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"org.scala-lang" % "scala-compiler" % scalaVersion.value, "org.scala-lang" % "scala-compiler" % scalaVersion.value,
"org.scala-lang" % "scala-reflect" % scalaVersion.value, "org.scala-lang" % "scala-reflect" % scalaVersion.value,
"org.scalatest" %% "scalatest" % scalatestVersion % Test "org.scalatest" %% "scalatest" % scalatestVersion % Test
), ),
Compile / scalacOptions ++= { Compile / scalacOptions ++= {
CrossVersion.partialVersion(scalaVersion.value) match { CrossVersion.partialVersion(scalaVersion.value) match {
...@@ -44,12 +44,12 @@ lazy val commonSettings = ...@@ -44,12 +44,12 @@ lazy val commonSettings =
case _ => List("-Ymacro-annotations", "-Ywarn-unused" /*, "-Ymacro-debug-verbose"*/ ) case _ => List("-Ymacro-annotations", "-Ywarn-unused" /*, "-Ymacro-debug-verbose"*/ )
} }
} ++ Seq("-language:experimental.macros"), } ++ Seq("-language:experimental.macros"),
Compile / compile := (Compile / compile).dependsOn(Compile / headerCreateAll).value, Compile / compile := (Compile / compile).dependsOn(Compile / headerCreateAll).value,
Global / onChangedBuildSource := ReloadOnSourceChanges, Global / onChangedBuildSource := ReloadOnSourceChanges,
headerLicense := Some(HeaderLicense.MIT("2022", "bitlap")), headerLicense := Some(HeaderLicense.MIT("2022", "bitlap")),
Test / testOptions += Tests.Argument("-oDF"), Test / testOptions += Tests.Argument("-oDF"),
Test / fork := true, Test / fork := true,
publishConfiguration := publishConfiguration.value.withOverwrite(true), publishConfiguration := publishConfiguration.value.withOverwrite(true),
publishLocalConfiguration := publishLocalConfiguration.value.withOverwrite(true) publishLocalConfiguration := publishLocalConfiguration.value.withOverwrite(true)
) )
...@@ -57,10 +57,10 @@ lazy val `smt-cacheable-core` = (project in file("smt-cacheable-core")) ...@@ -57,10 +57,10 @@ lazy val `smt-cacheable-core` = (project in file("smt-cacheable-core"))
.settings(commonSettings) .settings(commonSettings)
.settings(Publishing.publishSettings) .settings(Publishing.publishSettings)
.settings( .settings(
name := "smt-cacheable-core", name := "smt-cacheable-core",
crossScalaVersions := List(scala213, scala212), crossScalaVersions := List(scala213, scala212),
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"dev.zio" %% "zio" % zioVersion, // FIXME we should use compile or provide ??? "dev.zio" %% "zio" % zioVersion, // FIXME we should use compile or provide ???
"dev.zio" %% "zio-streams" % zioVersion, "dev.zio" %% "zio-streams" % zioVersion,
"dev.zio" %% "zio-logging" % zioLoggingVersion "dev.zio" %% "zio-logging" % zioLoggingVersion
) )
...@@ -72,10 +72,10 @@ lazy val `smt-cacheable-caffeine` = (project in file("smt-cacheable-caffeine")) ...@@ -72,10 +72,10 @@ lazy val `smt-cacheable-caffeine` = (project in file("smt-cacheable-caffeine"))
.settings(commonSettings) .settings(commonSettings)
.settings(Publishing.publishSettings) .settings(Publishing.publishSettings)
.settings( .settings(
name := "smt-cacheable-caffeine", name := "smt-cacheable-caffeine",
crossScalaVersions := List(scala213, scala212), crossScalaVersions := List(scala213, scala212),
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"com.typesafe" % "config" % configVersion, "com.typesafe" % "config" % configVersion,
"com.github.ben-manes.caffeine" % "caffeine" % caffeineVersion "com.github.ben-manes.caffeine" % "caffeine" % caffeineVersion
) )
) )
...@@ -87,14 +87,14 @@ lazy val `smt-cacheable-redis` = (project in file("smt-cacheable-redis")) ...@@ -87,14 +87,14 @@ lazy val `smt-cacheable-redis` = (project in file("smt-cacheable-redis"))
.settings(commonSettings) .settings(commonSettings)
.settings(Publishing.publishSettings) .settings(Publishing.publishSettings)
.settings( .settings(
name := "smt-cacheable-redis", name := "smt-cacheable-redis",
crossScalaVersions := List(scala213, scala212), crossScalaVersions := List(scala213, scala212),
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"dev.zio" %% "zio-redis" % zioRedisVersion, "dev.zio" %% "zio-redis" % zioRedisVersion,
"com.typesafe" % "config" % configVersion, "com.typesafe" % "config" % configVersion,
"dev.zio" %% "zio-schema" % zioSchemaVersion, "dev.zio" %% "zio-schema" % zioSchemaVersion,
"dev.zio" %% "zio-schema-json" % zioSchemaVersion, "dev.zio" %% "zio-schema-json" % zioSchemaVersion,
"dev.zio" %% "zio-schema-derivation" % zioSchemaVersion % Test "dev.zio" %% "zio-schema-derivation" % zioSchemaVersion % Test
) )
) )
.dependsOn(`smt-cacheable-core` % "compile->compile;test->test") .dependsOn(`smt-cacheable-core` % "compile->compile;test->test")
...@@ -104,7 +104,7 @@ lazy val `smt-cacheable-redis` = (project in file("smt-cacheable-redis")) ...@@ -104,7 +104,7 @@ lazy val `smt-cacheable-redis` = (project in file("smt-cacheable-redis"))
lazy val `smt-benchmark` = (project in file("smt-benchmark")) lazy val `smt-benchmark` = (project in file("smt-benchmark"))
.settings(commonSettings) .settings(commonSettings)
.settings( .settings(
name := "smt-benchmark", name := "smt-benchmark",
publish / skip := true publish / skip := true
) )
.dependsOn(`smt-cacheable-core`, `smt-cacheable-redis`, `smt-cacheable-caffeine`) .dependsOn(`smt-cacheable-core`, `smt-cacheable-redis`, `smt-cacheable-caffeine`)
...@@ -114,7 +114,7 @@ lazy val `smt-benchmark` = (project in file("smt-benchmark")) ...@@ -114,7 +114,7 @@ lazy val `smt-benchmark` = (project in file("smt-benchmark"))
lazy val `smt-csv-core` = (project in file("smt-csv-core")) lazy val `smt-csv-core` = (project in file("smt-csv-core"))
.settings(commonSettings) .settings(commonSettings)
.settings( .settings(
name := "smt-csv-core", name := "smt-csv-core",
crossScalaVersions := List(scala213, scala212, scala211) crossScalaVersions := List(scala213, scala212, scala211)
) )
.settings(Publishing.publishSettings) .settings(Publishing.publishSettings)
...@@ -124,7 +124,7 @@ lazy val `smt-csv-core` = (project in file("smt-csv-core")) ...@@ -124,7 +124,7 @@ lazy val `smt-csv-core` = (project in file("smt-csv-core"))
lazy val `smt-csv-derive` = (project in file("smt-csv-derive")) lazy val `smt-csv-derive` = (project in file("smt-csv-derive"))
.settings(commonSettings) .settings(commonSettings)
.settings( .settings(
name := "smt-csv-derive", name := "smt-csv-derive",
crossScalaVersions := List(scala213, scala212, scala211) crossScalaVersions := List(scala213, scala212, scala211)
) )
.settings(Publishing.publishSettings) .settings(Publishing.publishSettings)
...@@ -135,17 +135,17 @@ lazy val `smt-csv-derive` = (project in file("smt-csv-derive")) ...@@ -135,17 +135,17 @@ lazy val `smt-csv-derive` = (project in file("smt-csv-derive"))
lazy val `smt-tools` = (project in file("smt-tools")) lazy val `smt-tools` = (project in file("smt-tools"))
.settings(commonSettings) .settings(commonSettings)
.settings( .settings(
name := "smt-tools", name := "smt-tools",
crossScalaVersions := List(scala213, scala212, scala211), crossScalaVersions := List(scala213, scala212, scala211),
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion, "com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion,
"com.typesafe.play" %% "play-json" % playJsonVersion % Test, "com.typesafe.play" %% "play-json" % playJsonVersion % Test,
"org.apache.logging.log4j" % "log4j-api" % log4jVersion % Test, "org.apache.logging.log4j" % "log4j-api" % log4jVersion % Test,
"org.apache.logging.log4j" % "log4j-core" % log4jVersion % Test, "org.apache.logging.log4j" % "log4j-core" % log4jVersion % Test,
"org.apache.logging.log4j" % "log4j-slf4j-impl" % log4jVersion % Test, "org.apache.logging.log4j" % "log4j-slf4j-impl" % log4jVersion % Test,
"com.fasterxml.jackson.module" %% "jackson-module-scala" % jacksonScalaVersion % Test, "com.fasterxml.jackson.module" %% "jackson-module-scala" % jacksonScalaVersion % Test,
"com.alipay.sofa" % "jraft-core" % jraftVersion % Test, "com.alipay.sofa" % "jraft-core" % jraftVersion % Test,
"com.google.protobuf" % "protobuf-java" % protocVersion % Test "com.google.protobuf" % "protobuf-java" % protocVersion % Test
) )
) )
.settings(Publishing.publishSettings) .settings(Publishing.publishSettings)
...@@ -164,12 +164,12 @@ lazy val root = (project in file(".")) ...@@ -164,12 +164,12 @@ lazy val root = (project in file("."))
) )
.settings( .settings(
commands ++= Commands.value, commands ++= Commands.value,
crossScalaVersions := Nil, crossScalaVersions := Nil,
publish / skip := true, publish / skip := true,
headerLicense := Some(HeaderLicense.MIT("2022", "bitlap")), headerLicense := Some(HeaderLicense.MIT("2022", "bitlap")),
releaseIgnoreUntrackedFiles := true, releaseIgnoreUntrackedFiles := true,
releaseCrossBuild := false, //@see https://www.scala-sbt.org/1.x/docs/Cross-Build.html releaseCrossBuild := false, // @see https://www.scala-sbt.org/1.x/docs/Cross-Build.html
releaseTagName := (ThisBuild / version).value, releaseTagName := (ThisBuild / version).value,
releasePublishArtifactsAction := PgpKeys.publishSigned.value, releasePublishArtifactsAction := PgpKeys.publishSigned.value,
releaseProcess := Seq[ReleaseStep]( releaseProcess := Seq[ReleaseStep](
checkSnapshotDependencies, checkSnapshotDependencies,
...@@ -192,9 +192,9 @@ lazy val `scala2-13` = (project in file("examples/scala2-13")) ...@@ -192,9 +192,9 @@ lazy val `scala2-13` = (project in file("examples/scala2-13"))
.settings(scalaVersion := scala213) .settings(scalaVersion := scala213)
.settings( .settings(
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"org.bitlap" %% "smt-tools" % lastVersionForExamples, "org.bitlap" %% "smt-tools" % lastVersionForExamples,
"org.bitlap" %% "smt-cacheable-core" % lastVersionForExamples, "org.bitlap" %% "smt-cacheable-core" % lastVersionForExamples,
"org.bitlap" %% "smt-cacheable-redis" % lastVersionForExamples, "org.bitlap" %% "smt-cacheable-redis" % lastVersionForExamples,
"org.bitlap" %% "smt-cacheable-caffeine" % lastVersionForExamples "org.bitlap" %% "smt-cacheable-caffeine" % lastVersionForExamples
) )
) )
...@@ -207,9 +207,9 @@ lazy val `scala2-12` = (project in file("examples/scala2-12")) ...@@ -207,9 +207,9 @@ lazy val `scala2-12` = (project in file("examples/scala2-12"))
.settings(scalaVersion := scala212) .settings(scalaVersion := scala212)
.settings( .settings(
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"org.bitlap" %% "smt-tools" % lastVersionForExamples, "org.bitlap" %% "smt-tools" % lastVersionForExamples,
"org.bitlap" %% "smt-cacheable-core" % lastVersionForExamples, "org.bitlap" %% "smt-cacheable-core" % lastVersionForExamples,
"org.bitlap" %% "smt-cacheable-redis" % lastVersionForExamples, "org.bitlap" %% "smt-cacheable-redis" % lastVersionForExamples,
"org.bitlap" %% "smt-cacheable-caffeine" % lastVersionForExamples "org.bitlap" %% "smt-cacheable-caffeine" % lastVersionForExamples
) )
) )
......
import sbt.Command import sbt.Command
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2022/1/15 * @since 2022/1/15
* @version 1.0 * @version 1.0
*/ */
object Commands { object Commands {
......
...@@ -2,16 +2,16 @@ import sbt.Keys._ ...@@ -2,16 +2,16 @@ import sbt.Keys._
import sbt._ import sbt._
import xerial.sbt.Sonatype.autoImport._ import xerial.sbt.Sonatype.autoImport._
/** /** sbt publish setting
* sbt publish setting
* *
* @author 梦境迷离 dreamylost * @author
* @since 2020-07-19 * 梦境迷离 dreamylost
* @version v1.0 * @since 2020-07-19
* @version v1.0
*/ */
object Publishing { object Publishing {
//publish by sbt publishSigned // publish by sbt publishSigned
lazy val publishSettings = Seq( lazy val publishSettings = Seq(
isSnapshot := version.value endsWith "SNAPSHOT", isSnapshot := version.value endsWith "SNAPSHOT",
credentials += Credentials(Path.userHome / ".ivy2" / ".bitlap_sonatype_credentials"), credentials += Credentials(Path.userHome / ".ivy2" / ".bitlap_sonatype_credentials"),
...@@ -22,10 +22,10 @@ object Publishing { ...@@ -22,10 +22,10 @@ object Publishing {
else else
Some("releases" at nexus + "service/local/staging/deploy/maven2") Some("releases" at nexus + "service/local/staging/deploy/maven2")
}, },
licenses := Seq(License.MIT), licenses := Seq(License.MIT),
publishMavenStyle := true, publishMavenStyle := true,
Test / publishArtifact := false, Test / publishArtifact := false,
pomIncludeRepository := { _ => false }, pomIncludeRepository := { _ => false },
developers := List( developers := List(
Developer( Developer(
id = "dreamylost", id = "dreamylost",
...@@ -41,7 +41,7 @@ object Publishing { ...@@ -41,7 +41,7 @@ object Publishing {
) )
), ),
sonatypeProfileName := organization.value, sonatypeProfileName := organization.value,
homepage := Some(url("https://bitlap.org")), homepage := Some(url("https://bitlap.org")),
scmInfo := Some( scmInfo := Some(
ScmInfo( ScmInfo(
url("https://github.com/bitlap/scala-macro-tools"), url("https://github.com/bitlap/scala-macro-tools"),
......
addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.12") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.12")
addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2") addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2")
addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.13") addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.13")
addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.7.0") addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.7.0")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.9.3") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.9.3")
addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.3") addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.3")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6")
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.10.0") addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.10.0")
...@@ -24,11 +24,11 @@ package org.bitlap.cacheable.benchmark ...@@ -24,11 +24,11 @@ package org.bitlap.cacheable.benchmark
import zio.{ BootstrapRuntime, ZIO } import zio.{ BootstrapRuntime, ZIO }
import zio.internal.Platform import zio.internal.Platform
/** /** runtime
* runtime
* *
* @author 梦境迷离 * @author
* @version 1.0,2022/3/22 * 梦境迷离
* @version 1.0,2022/3/22
*/ */
trait BenchmarkRuntime extends BootstrapRuntime { trait BenchmarkRuntime extends BootstrapRuntime {
......
...@@ -30,11 +30,11 @@ import zio.ZIO ...@@ -30,11 +30,11 @@ import zio.ZIO
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import scala.util.Random import scala.util.Random
/** /** benchmark @cacheable
* benchmark @cacheable
* *
* @author 梦境迷离 * @author
* @version 1.0,2022/3/22 * 梦境迷离
* @version 1.0,2022/3/22
*/ */
@State(Scope.Thread) @State(Scope.Thread)
@BenchmarkMode(Array(Mode.Throughput)) @BenchmarkMode(Array(Mode.Throughput))
......
...@@ -28,11 +28,11 @@ import org.bitlap.cacheable.core._ ...@@ -28,11 +28,11 @@ import org.bitlap.cacheable.core._
import zio.ZIO import zio.ZIO
import zio.stream.ZStream import zio.stream.ZStream
/** /** redis cache
* redis cache
* *
* @author 梦境迷离 * @author
* @version 1.0,2022/3/21 * 梦境迷离
* @version 1.0,2022/3/21
*/ */
object Implicits { object Implicits {
...@@ -53,10 +53,10 @@ object Implicits { ...@@ -53,10 +53,10 @@ object Implicits {
override def getIfPresent( override def getIfPresent(
business: => ZStream[Any, Throwable, T] business: => ZStream[Any, Throwable, T]
)(identities: List[String], args: List[_]): ZStream[Any, Throwable, T] = { )(identities: List[String], args: List[_]): ZStream[Any, Throwable, T] = {
val key = cacheKey(identities) val key = cacheKey(identities)
val field = cacheField(args) val field = cacheField(args)
val syncResultFuture = zio.Runtime.global.unsafeRunToFuture(business.runCollect) val syncResultFuture = zio.Runtime.global.unsafeRunToFuture(business.runCollect)
lazy val result = Await.result(syncResultFuture, ZCaffeine.calculateResultTimeout) lazy val result = Await.result(syncResultFuture, ZCaffeine.calculateResultTimeout)
for { for {
chunk <- ZStream.fromEffect( chunk <- ZStream.fromEffect(
ZCaffeine ZCaffeine
...@@ -95,7 +95,7 @@ object Implicits { ...@@ -95,7 +95,7 @@ object Implicits {
override def getIfPresent( override def getIfPresent(
business: => ZIO[Any, Throwable, T] business: => ZIO[Any, Throwable, T]
)(identities: List[String], args: List[_]): ZIO[Any, Throwable, T] = { )(identities: List[String], args: List[_]): ZIO[Any, Throwable, T] = {
val key = cacheKey(identities) val key = cacheKey(identities)
val field = cacheField(args) val field = cacheField(args)
for { for {
cacheValue <- ZCaffeine.hGet[T](key, field) cacheValue <- ZCaffeine.hGet[T](key, field)
......
...@@ -28,15 +28,15 @@ import org.bitlap.cacheable.core.Utils ...@@ -28,15 +28,15 @@ import org.bitlap.cacheable.core.Utils
import java.util.concurrent.{ ConcurrentHashMap, TimeUnit } import java.util.concurrent.{ ConcurrentHashMap, TimeUnit }
import scala.concurrent.duration.Duration import scala.concurrent.duration.Duration
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/3/21 * @version 1.0,2022/3/21
*/ */
object ZCaffeine { object ZCaffeine {
import zio.Task import zio.Task
private val conf: Config = ConfigFactory.load("reference.conf") private val conf: Config = ConfigFactory.load("reference.conf")
private val custom: Config = ConfigFactory.load("application.conf").withFallback(conf) private val custom: Config = ConfigFactory.load("application.conf").withFallback(conf)
private[caffeine] lazy val disabledLog: Boolean = custom.getBoolean("caffeine.disabledLog") private[caffeine] lazy val disabledLog: Boolean = custom.getBoolean("caffeine.disabledLog")
...@@ -44,7 +44,7 @@ object ZCaffeine { ...@@ -44,7 +44,7 @@ object ZCaffeine {
custom.getString("caffeine.calculateResultTimeout") custom.getString("caffeine.calculateResultTimeout")
) )
private lazy val maximumSize: Int = custom.getInt("caffeine.maximumSize") private lazy val maximumSize: Int = custom.getInt("caffeine.maximumSize")
private lazy val expireAfterWriteSeconds: Int = custom.getInt("caffeine.expireAfterWriteSeconds") private lazy val expireAfterWriteSeconds: Int = custom.getInt("caffeine.expireAfterWriteSeconds")
val hashCache: Cache[String, ConcurrentHashMap[String, Any]] = Caffeine val hashCache: Cache[String, ConcurrentHashMap[String, Any]] = Caffeine
......
...@@ -29,10 +29,10 @@ import zio.stream.ZStream ...@@ -29,10 +29,10 @@ import zio.stream.ZStream
import scala.util.Random import scala.util.Random
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2022/3/19 * @since 2022/3/19
* @version 1.0 * @version 1.0
*/ */
class CacheEvictTest extends AnyFlatSpec with Matchers { class CacheEvictTest extends AnyFlatSpec with Matchers {
...@@ -45,7 +45,7 @@ class CacheEvictTest extends AnyFlatSpec with Matchers { ...@@ -45,7 +45,7 @@ class CacheEvictTest extends AnyFlatSpec with Matchers {
def readIOFunction: String = "hello world" def readIOFunction: String = "hello world"
val readIOMethodName = "readIOFunction" val readIOMethodName = "readIOFunction"
val readStreamMethodName = "readStreamFunction" val readStreamMethodName = "readStreamFunction"
"cacheEvict1" should "expected annotation pattern" in { "cacheEvict1" should "expected annotation pattern" in {
...@@ -93,10 +93,10 @@ class CacheEvictTest extends AnyFlatSpec with Matchers { ...@@ -93,10 +93,10 @@ class CacheEvictTest extends AnyFlatSpec with Matchers {
ZIO.effect(Random.nextInt() + "") ZIO.effect(Random.nextInt() + "")
val result = runtime.unsafeRun(for { val result = runtime.unsafeRun(for {
_ <- ZCaffeine.del("CacheableTest-" + readIOMethodName) _ <- ZCaffeine.del("CacheableTest-" + readIOMethodName)
read <- readIOFunction(1, "hello") read <- readIOFunction(1, "hello")
update <- updateIOFunction(1, "hello") update <- updateIOFunction(1, "hello")
cache <- ZCaffeine.hGet[String]("CacheableTest-" + readIOMethodName, "1-hello") cache <- ZCaffeine.hGet[String]("CacheableTest-" + readIOMethodName, "1-hello")
} yield cache) } yield cache)
result shouldEqual None result shouldEqual None
} }
...@@ -113,10 +113,10 @@ class CacheEvictTest extends AnyFlatSpec with Matchers { ...@@ -113,10 +113,10 @@ class CacheEvictTest extends AnyFlatSpec with Matchers {
ZStream.fromEffect(ZIO.effect(Random.nextInt() + "")) ZStream.fromEffect(ZIO.effect(Random.nextInt() + ""))
val result = runtime.unsafeRun(for { val result = runtime.unsafeRun(for {
_ <- ZCaffeine.del("CacheableTest-" + readStreamMethodName) _ <- ZCaffeine.del("CacheableTest-" + readStreamMethodName)
read <- readStreamFunction(1, "hello").runHead read <- readStreamFunction(1, "hello").runHead
update <- updateStreamFunction(1, "hello").runHead update <- updateStreamFunction(1, "hello").runHead
cache <- ZCaffeine.hGet[String]("CacheableTest-" + readStreamMethodName, "1-hello") cache <- ZCaffeine.hGet[String]("CacheableTest-" + readStreamMethodName, "1-hello")
} yield cache) } yield cache)
result shouldEqual None result shouldEqual None
} }
......
...@@ -30,10 +30,10 @@ import zio.{ Chunk, Task, ZIO } ...@@ -30,10 +30,10 @@ import zio.{ Chunk, Task, ZIO }
import java.util import java.util
import scala.util.Random import scala.util.Random
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2021/8/7 * @since 2021/8/7
* @version 1.0 * @version 1.0
*/ */
class CacheableTest extends AnyFlatSpec with Matchers { class CacheableTest extends AnyFlatSpec with Matchers {
...@@ -89,9 +89,9 @@ class CacheableTest extends AnyFlatSpec with Matchers { ...@@ -89,9 +89,9 @@ class CacheableTest extends AnyFlatSpec with Matchers {
ZIO.effect(cacheValue) ZIO.effect(cacheValue)
val result = runtime.unsafeRun(for { val result = runtime.unsafeRun(for {
_ <- ZCaffeine.del("CacheableTest-readIOFunction") _ <- ZCaffeine.del("CacheableTest-readIOFunction")
method <- readIOFunction(1, "hello") method <- readIOFunction(1, "hello")
cache <- ZCaffeine.hGet[String]("CacheableTest-readIOFunction", "1-hello") cache <- ZCaffeine.hGet[String]("CacheableTest-readIOFunction", "1-hello")
} yield method -> cache) } yield method -> cache)
Some(result._1) shouldEqual result._2 Some(result._1) shouldEqual result._2
} }
...@@ -119,10 +119,10 @@ class CacheableTest extends AnyFlatSpec with Matchers { ...@@ -119,10 +119,10 @@ class CacheableTest extends AnyFlatSpec with Matchers {
) )
val result = runtime.unsafeRun(for { val result = runtime.unsafeRun(for {
_ <- ZCaffeine.del("CacheableTest-readIOFunction") _ <- ZCaffeine.del("CacheableTest-readIOFunction")
method <- readIOFunction(globalId.toInt).runHead method <- readIOFunction(globalId.toInt).runHead
update <- updateIOFunction("lisi").runHead update <- updateIOFunction("lisi").runHead
after <- readIOFunction(globalId.toInt).runHead after <- readIOFunction(globalId.toInt).runHead
} yield Some("lisi") -> after.map(_.name)) } yield Some("lisi") -> after.map(_.name))
result._1 shouldEqual result._2 result._1 shouldEqual result._2
} }
...@@ -142,10 +142,10 @@ class CacheableTest extends AnyFlatSpec with Matchers { ...@@ -142,10 +142,10 @@ class CacheableTest extends AnyFlatSpec with Matchers {
val newId = Random.nextInt().toString val newId = Random.nextInt().toString
val result = runtime.unsafeRun(for { val result = runtime.unsafeRun(for {
_ <- ZCaffeine.del("CacheableTest-readIOFunction") _ <- ZCaffeine.del("CacheableTest-readIOFunction")
method <- readIOFunction(globalId.toInt).runHead method <- readIOFunction(globalId.toInt).runHead
update <- updateIOFunction(newId).runHead update <- updateIOFunction(newId).runHead
after <- readIOFunction(globalId.toInt).runHead after <- readIOFunction(globalId.toInt).runHead
} yield Some(newId) -> after.map(_.id)) } yield Some(newId) -> after.map(_.id))
result._1 shouldEqual result._2 result._1 shouldEqual result._2
} }
...@@ -159,9 +159,9 @@ class CacheableTest extends AnyFlatSpec with Matchers { ...@@ -159,9 +159,9 @@ class CacheableTest extends AnyFlatSpec with Matchers {
println(chunk) println(chunk)
val result = runtime.unsafeRun(for { val result = runtime.unsafeRun(for {
_ <- ZCaffeine.del("CacheableTest-readIOFunction") _ <- ZCaffeine.del("CacheableTest-readIOFunction")
method <- readIOFunction(1, "hello") method <- readIOFunction(1, "hello")
cache <- ZCaffeine.hGet[Chunk[String]]("CacheableTest-readIOFunction", "1-hello").map(_.getOrElse(Chunk.empty)) cache <- ZCaffeine.hGet[Chunk[String]]("CacheableTest-readIOFunction", "1-hello").map(_.getOrElse(Chunk.empty))
} yield method -> cache) } yield method -> cache)
result._1 shouldEqual result._2 result._1 shouldEqual result._2
} }
......
...@@ -28,10 +28,10 @@ import zio.ZIO ...@@ -28,10 +28,10 @@ import zio.ZIO
import scala.util.Random import scala.util.Random
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2022/3/20 * @since 2022/3/20
* @version 1.0 * @version 1.0
*/ */
class CustomCacheableTest extends AnyFlatSpec with Matchers { class CustomCacheableTest extends AnyFlatSpec with Matchers {
......
...@@ -24,45 +24,52 @@ package org.bitlap.cacheable.core ...@@ -24,45 +24,52 @@ package org.bitlap.cacheable.core
import zio.ZIO import zio.ZIO
import zio.stream.ZStream import zio.stream.ZStream
/** /** A distributed cache for zio.
* A distributed cache for zio.
* *
* @tparam Z The result type of the function that returns the ZIO or ZStream effect. * @tparam Z
* The result type of the function that returns the ZIO or ZStream effect.
*/ */
trait Cache[Z] { trait Cache[Z] {
/** /** Get cache or getAndSet cache from Cache while read data.
* Get cache or getAndSet cache from Cache while read data.
* *
* @param business The function that returns the ZIO or ZStream effect. * @param business
* @param identities Append all strings for cache key. * The function that returns the ZIO or ZStream effect.
* @param args The parameters of the business function. * @param identities
* @return The result fo the business function. * Append all strings for cache key.
* @param args
* The parameters of the business function.
* @return
* The result fo the business function.
*/ */
def getIfPresent(business: => Z)(identities: List[String], args: List[_]): Z def getIfPresent(business: => Z)(identities: List[String], args: List[_]): Z
/** /** Evict cache while data update.
* Evict cache while data update.
* *
* @param business The function that returns the ZIO or ZStream effect. * @param business
* @param identities Append all strings for cache key. * The function that returns the ZIO or ZStream effect.
* @return The result fo the business function. * @param identities
* Append all strings for cache key.
* @return
* The result fo the business function.
*/ */
def evict(business: => Z)(identities: List[String]): Z def evict(business: => Z)(identities: List[String]): Z
/** /** Build a string for cache key.
* Build a string for cache key.
* *
* @param keys Append all strings for hash key * @param keys
* @return string * Append all strings for hash key
* @return
* string
*/ */
def cacheKey(keys: List[String]): String = keys.mkString("-") def cacheKey(keys: List[String]): String = keys.mkString("-")
/** /** Build a string for cache field.
* Build a string for cache field.
* *
* @param args The parameters of the business function. * @param args
* @return hash field * The parameters of the business function.
* @return
* hash field
*/ */
def cacheField(args: List[_]): String = args.map(_.toString).mkString("-") def cacheField(args: List[_]): String = args.map(_.toString).mkString("-")
......
...@@ -28,11 +28,11 @@ import zio.stream.{ UStream, ZStream } ...@@ -28,11 +28,11 @@ import zio.stream.{ UStream, ZStream }
import zio.{ UIO, ULayer, URLayer, ZIO } import zio.{ UIO, ULayer, URLayer, ZIO }
import zio.blocking.Blocking import zio.blocking.Blocking
/** /** Internal Utils
* Internal Utils
* *
* @author 梦境迷离 * @author
* @version 1.0,2022/3/18 * 梦境迷离
* @version 1.0,2022/3/18
*/ */
object Utils { object Utils {
......
...@@ -23,11 +23,11 @@ package org.bitlap.cacheable.core ...@@ -23,11 +23,11 @@ package org.bitlap.cacheable.core
import zio.ZIO import zio.ZIO
/** /** Redis Cache for ZIO.
* Redis Cache for ZIO.
* *
* @author 梦境迷离 * @author
* @version 2.0,2022/3/18 * 梦境迷离
* @version 2.0,2022/3/18
*/ */
trait ZIOCache[R, E, T] extends Cache[ZIO[R, E, T]] { trait ZIOCache[R, E, T] extends Cache[ZIO[R, E, T]] {
......
...@@ -23,11 +23,11 @@ package org.bitlap.cacheable.core ...@@ -23,11 +23,11 @@ package org.bitlap.cacheable.core
import zio.ZIO import zio.ZIO
/** /** Redis Update Cache for ZIO.
* Redis Update Cache for ZIO.
* *
* @author 梦境迷离 * @author
* @version 2.0,2022/3/18 * 梦境迷离
* @version 2.0,2022/3/18
*/ */
trait ZIOUpdateCache[R, E, T] extends Cache[ZIO[R, E, T]] { trait ZIOUpdateCache[R, E, T] extends Cache[ZIO[R, E, T]] {
......
...@@ -23,11 +23,11 @@ package org.bitlap.cacheable.core ...@@ -23,11 +23,11 @@ package org.bitlap.cacheable.core
import zio.stream.ZStream import zio.stream.ZStream
/** /** Redis Cache for ZStream.
* Redis Cache for ZStream.
* *
* @author 梦境迷离 * @author
* @version 2.0,2022/3/19 * 梦境迷离
* @version 2.0,2022/3/19
*/ */
trait ZStreamCache[R, E, T] extends Cache[ZStream[R, E, T]] { trait ZStreamCache[R, E, T] extends Cache[ZStream[R, E, T]] {
......
...@@ -23,11 +23,11 @@ package org.bitlap.cacheable.core ...@@ -23,11 +23,11 @@ package org.bitlap.cacheable.core
import zio.stream.ZStream import zio.stream.ZStream
/** /** Redis Update Cache for ZStream.
* Redis Update Cache for ZStream.
* *
* @author 梦境迷离 * @author
* @version 2.0,2022/3/19 * 梦境迷离
* @version 2.0,2022/3/19
*/ */
trait ZStreamUpdateCache[R, E, T] extends Cache[ZStream[R, E, T]] { trait ZStreamUpdateCache[R, E, T] extends Cache[ZStream[R, E, T]] {
......
...@@ -25,14 +25,16 @@ import org.bitlap.cacheable.core.macros.CacheEvictMacro ...@@ -25,14 +25,16 @@ import org.bitlap.cacheable.core.macros.CacheEvictMacro
import scala.annotation.{ compileTimeOnly, StaticAnnotation } import scala.annotation.{ compileTimeOnly, StaticAnnotation }
/** /** A distributed cache for zio.
* A distributed cache for zio.
* *
* @author 梦境迷离 * @author
* @param local Whether to enable local cache by caffeine. Must be a named parameter. * 梦境迷离
* @param values Indicates which caches the purge operation occurs on. Must be a named parameter. * @param local
* @since 2022/3/18 * Whether to enable local cache by caffeine. Must be a named parameter.
* @version 1.0 * @param values
* Indicates which caches the purge operation occurs on. Must be a named parameter.
* @since 2022/3/18
* @version 1.0
*/ */
@compileTimeOnly("enable macro to expand macro annotations") @compileTimeOnly("enable macro to expand macro annotations")
final class cacheEvict(local: Boolean = true, values: List[String] = Nil) extends StaticAnnotation { final class cacheEvict(local: Boolean = true, values: List[String] = Nil) extends StaticAnnotation {
......
...@@ -25,13 +25,14 @@ import org.bitlap.cacheable.core.macros.CacheableMacro.CacheableProcessor ...@@ -25,13 +25,14 @@ import org.bitlap.cacheable.core.macros.CacheableMacro.CacheableProcessor
import scala.annotation.{ compileTimeOnly, StaticAnnotation } import scala.annotation.{ compileTimeOnly, StaticAnnotation }
/** /** A distributed cache for zio.
* A distributed cache for zio.
* *
* @author 梦境迷离 * @author
* @param local Whether to enable local cache by caffeine. * 梦境迷离
* @since 2022/3/18 * @param local
* @version 1.0 * Whether to enable local cache by caffeine.
* @since 2022/3/18
* @version 1.0
*/ */
@compileTimeOnly("enable macro to expand macro annotations") @compileTimeOnly("enable macro to expand macro annotations")
final class cacheable(local: Boolean = true) extends StaticAnnotation { final class cacheable(local: Boolean = true) extends StaticAnnotation {
......
...@@ -27,20 +27,19 @@ import scala.annotation.tailrec ...@@ -27,20 +27,19 @@ import scala.annotation.tailrec
import scala.collection.mutable.ListBuffer import scala.collection.mutable.ListBuffer
import scala.reflect.macros.whitebox import scala.reflect.macros.whitebox
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2022/3/19 * @since 2022/3/19
* @version 1.0 * @version 1.0
*/ */
abstract class AbstractMacroProcessor(val c: whitebox.Context) { abstract class AbstractMacroProcessor(val c: whitebox.Context) {
import c.universe._ import c.universe._
/** /** Output ast result.
* Output ast result.
* *
* @param force * @param force
* @param resTree * @param resTree
*/ */
def printTree(force: Boolean, resTree: Tree): Unit = def printTree(force: Boolean, resTree: Tree): Unit =
c.info( c.info(
...@@ -50,19 +49,19 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) { ...@@ -50,19 +49,19 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) {
force = false force = false
) )
/** /** Find the specified method Name in the enclosing class definition.
* Find the specified method Name in the enclosing class definition.
* *
* @param t * @param t
* @return Return a optional [[scala.reflect.api.Names#TermName]] * @return
* Return a optional [[scala.reflect.api.Names#TermName]]
*/ */
def findDefDefInEnclosingClass(t: Name): Option[TermName] = def findDefDefInEnclosingClass(t: Name): Option[TermName] =
getDefDefInEnclosingClass.find(_.decodedName.toString == t.decodedName.toString) getDefDefInEnclosingClass.find(_.decodedName.toString == t.decodedName.toString)
/** /** Find all method Name in the enclosing class definition.
* Find all method Name in the enclosing class definition.
* *
* @return Return a sequence of [[scala.reflect.api.Names#TermName]] * @return
* Return a sequence of [[scala.reflect.api.Names#TermName]]
*/ */
def getDefDefInEnclosingClass: Set[TermName] = { def getDefDefInEnclosingClass: Set[TermName] = {
val buffer = ListBuffer[TermName]() val buffer = ListBuffer[TermName]()
...@@ -92,10 +91,9 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) { ...@@ -92,10 +91,9 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) {
buffer.result().toSet buffer.result().toSet
} }
/** /** Get enclosing class name
* Get enclosing class name
* *
* @return * @return
*/ */
def getEnclosingClassName: String = def getEnclosingClassName: String =
c.enclosingClass match { c.enclosingClass match {
......
...@@ -23,12 +23,12 @@ package org.bitlap.cacheable.core.macros ...@@ -23,12 +23,12 @@ package org.bitlap.cacheable.core.macros
import scala.reflect.macros.whitebox import scala.reflect.macros.whitebox
/** /** Evict cache
* Evict cache
* *
* @author 梦境迷离 * @author
* @since 2022/3/19 * 梦境迷离
* @version 1.0 * @since 2022/3/19
* @version 1.0
*/ */
object CacheEvictMacro { object CacheEvictMacro {
...@@ -37,9 +37,9 @@ object CacheEvictMacro { ...@@ -37,9 +37,9 @@ object CacheEvictMacro {
import c.universe._ import c.universe._
private lazy val resultValName: c.universe.TermName = TermName("$result") private lazy val resultValName: c.universe.TermName = TermName("$result")
private lazy val argsValName: c.universe.TermName = TermName("$args") private lazy val argsValName: c.universe.TermName = TermName("$args")
private val parameters: Tuple2[Boolean, List[String]] = { private val parameters: Tuple2[Boolean, List[String]] =
c.prefix.tree match { c.prefix.tree match {
case q"new cacheEvict(local=$local, values=$values)" => case q"new cacheEvict(local=$local, values=$values)" =>
Tuple2( Tuple2(
...@@ -69,7 +69,6 @@ object CacheEvictMacro { ...@@ -69,7 +69,6 @@ object CacheEvictMacro {
case _ => case _ =>
c.abort(c.enclosingPosition, "Unexpected annotation pattern!") c.abort(c.enclosingPosition, "Unexpected annotation pattern!")
} }
}
def impl(annottees: c.universe.Expr[Any]*): c.universe.Expr[Any] = { def impl(annottees: c.universe.Expr[Any]*): c.universe.Expr[Any] = {
val resTree = annottees.map(_.tree) match { val resTree = annottees.map(_.tree) match {
...@@ -107,9 +106,9 @@ object CacheEvictMacro { ...@@ -107,9 +106,9 @@ object CacheEvictMacro {
c.info( c.info(
c.enclosingPosition, c.enclosingPosition,
s"""These methods will remove from cache: $identities, key prefix is: $enclosingClassName, mode is: ${if ( s"""These methods will remove from cache: $identities, key prefix is: $enclosingClassName, mode is: ${if (
parameters._1 parameters._1
) "local" ) "local"
else "redis"}""", else "redis"}""",
true true
) )
val newBody = val newBody =
......
...@@ -23,12 +23,12 @@ package org.bitlap.cacheable.core.macros ...@@ -23,12 +23,12 @@ package org.bitlap.cacheable.core.macros
import scala.reflect.macros.whitebox import scala.reflect.macros.whitebox
/** /** GetAndSet cache
* GetAndSet cache
* *
* @author 梦境迷离 * @author
* @since 2022/3/18 * 梦境迷离
* @version 1.0 * @since 2022/3/18
* @version 1.0
*/ */
object CacheableMacro { object CacheableMacro {
...@@ -37,16 +37,15 @@ object CacheableMacro { ...@@ -37,16 +37,15 @@ object CacheableMacro {
import c.universe._ import c.universe._
private lazy val resultValName: c.universe.TermName = TermName("$result") private lazy val resultValName: c.universe.TermName = TermName("$result")
private lazy val keyValName: c.universe.TermName = TermName("$key") private lazy val keyValName: c.universe.TermName = TermName("$key")
protected val local: Boolean = { protected val local: Boolean =
c.prefix.tree match { c.prefix.tree match {
case q"new cacheable(local=$local)" => case q"new cacheable(local=$local)" =>
c.eval(c.Expr[Boolean](c.untypecheck(local.asInstanceOf[Tree].duplicate))) c.eval(c.Expr[Boolean](c.untypecheck(local.asInstanceOf[Tree].duplicate)))
case q"new cacheable($local)" => c.eval(c.Expr[Boolean](c.untypecheck(local.asInstanceOf[Tree].duplicate))) case q"new cacheable($local)" => c.eval(c.Expr[Boolean](c.untypecheck(local.asInstanceOf[Tree].duplicate)))
case q"new cacheable()" => true case q"new cacheable()" => true
} }
}
private def getParamsName(vparamss: List[List[ValDef]]): List[List[TermName]] = private def getParamsName(vparamss: List[List[ValDef]]): List[List[TermName]] =
vparamss.map(_.map(_.name)) vparamss.map(_.map(_.name))
......
...@@ -28,11 +28,11 @@ import zio.stream.ZStream ...@@ -28,11 +28,11 @@ import zio.stream.ZStream
import zio.Chunk import zio.Chunk
import java.util.concurrent.atomic.AtomicLong import java.util.concurrent.atomic.AtomicLong
/** /** redis cache
* redis cache
* *
* @author 梦境迷离 * @author
* @version 1.0,2022/3/21 * 梦境迷离
* @version 1.0,2022/3/21
*/ */
object Implicits { object Implicits {
...@@ -55,24 +55,25 @@ object Implicits { ...@@ -55,24 +55,25 @@ object Implicits {
override def getIfPresent( override def getIfPresent(
business: => ZStream[Any, Throwable, T] business: => ZStream[Any, Throwable, T]
)(identities: List[String], args: List[_]): ZStream[Any, Throwable, T] = { )(identities: List[String], args: List[_]): ZStream[Any, Throwable, T] = {
val key = cacheKey(identities) val key = cacheKey(identities)
val field = cacheField(args) val field = cacheField(args)
lazy val ret = business.runCollect.tap(r => ZRedisService.hSet[Chunk[T]](key, field, r)) lazy val ret = business.runCollect.tap(r => ZRedisService.hSet[Chunk[T]](key, field, r))
lazy val resultFun = (chunk: Chunk[T]) => if (chunk.isEmpty) ret else ZIO.succeed(chunk) lazy val resultFun = (chunk: Chunk[T]) => if (chunk.isEmpty) ret else ZIO.succeed(chunk)
lazy val count = new AtomicLong(0L) lazy val count = new AtomicLong(0L)
for { for {
// TODO fix it, cannot get case class from redis and not lock // TODO fix it, cannot get case class from redis and not lock
cacheValue <- ZStream.fromEffect(ZRedisService.hGet[Chunk[T]](key, field)).map(_.getOrElse(Chunk.empty)) cacheValue <- ZStream.fromEffect(ZRedisService.hGet[Chunk[T]](key, field)).map(_.getOrElse(Chunk.empty))
_ <- Utils _ <- Utils
.debugS(s"Redis ZStream getIfPresent >>> identity:[$key],field:[$field],cacheValue:[$cacheValue]") .debugS(s"Redis ZStream getIfPresent >>> identity:[$key],field:[$field],cacheValue:[$cacheValue]")
.when(!ZRedisConfiguration.disabledLog) .when(!ZRedisConfiguration.disabledLog)
ret <- ZStream.fromEffect(resultFun(cacheValue)) ret <- ZStream.fromEffect(resultFun(cacheValue))
result <- ZStream.fromIterable(ret) result <- ZStream.fromIterable(ret)
_ <- Utils _ <-
.debugS( Utils
s"Redis ZStream getIfPresent >>> identity:[$key],field(${count.incrementAndGet()}):[$field],result:[$result]" .debugS(
) s"Redis ZStream getIfPresent >>> identity:[$key],field(${count.incrementAndGet()}):[$field],result:[$result]"
.when(!ZRedisConfiguration.disabledLog) )
.when(!ZRedisConfiguration.disabledLog)
} yield result } yield result
} }
} }
...@@ -91,7 +92,7 @@ object Implicits { ...@@ -91,7 +92,7 @@ object Implicits {
override def getIfPresent( override def getIfPresent(
business: => ZIO[Any, Throwable, T] business: => ZIO[Any, Throwable, T]
)(identities: List[String], args: List[_]): ZIO[Any, Throwable, T] = { )(identities: List[String], args: List[_]): ZIO[Any, Throwable, T] = {
val key = cacheKey(identities) val key = cacheKey(identities)
val field = cacheField(args) val field = cacheField(args)
for { for {
cacheValue <- ZRedisService.hGet[T](key, field) cacheValue <- ZRedisService.hGet[T](key, field)
......
...@@ -28,17 +28,17 @@ import zio.schema.codec.{ Codec, JsonCodec } ...@@ -28,17 +28,17 @@ import zio.schema.codec.{ Codec, JsonCodec }
import zio.{ Has, Layer, ULayer, ZLayer } import zio.{ Has, Layer, ULayer, ZLayer }
import zio.logging.Logging import zio.logging.Logging
/** /** redis configuration
* redis configuration
* *
* @author 梦境迷离 * @author
* @since 2022/1/10 * 梦境迷离
* @version 2.0 * @since 2022/1/10
* @version 2.0
*/ */
object ZRedisConfiguration { object ZRedisConfiguration {
private lazy val conf: Config = ConfigFactory.load("reference.conf") private lazy val conf: Config = ConfigFactory.load("reference.conf")
private lazy val custom: Config = ConfigFactory.load("application.conf").withFallback(conf) private lazy val custom: Config = ConfigFactory.load("application.conf").withFallback(conf)
private lazy val redisConf: RedisConfig = RedisConfig(custom.getString("redis.host"), custom.getInt("redis.port")) private lazy val redisConf: RedisConfig = RedisConfig(custom.getString("redis.host"), custom.getInt("redis.port"))
private[redis] lazy val disabledLog: Boolean = custom.getBoolean("redis.disabledLog") private[redis] lazy val disabledLog: Boolean = custom.getBoolean("redis.disabledLog")
......
...@@ -25,10 +25,11 @@ import zio.{ redis, Has, ULayer, ZIO, ZLayer } ...@@ -25,10 +25,11 @@ import zio.{ redis, Has, ULayer, ZIO, ZLayer }
import zio.redis.{ Redis, RedisError } import zio.redis.{ Redis, RedisError }
import zio.schema.Schema import zio.schema.Schema
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @see https://zio.dev/version-1.x/datatypes/contextual/#module-pattern-20 * @see
* @version 2.0,2022/1/17 * https://zio.dev/version-1.x/datatypes/contextual/#module-pattern-20
* @version 2.0,2022/1/17
*/ */
case class ZRedisLive(private val rs: Redis) extends ZRedisService { case class ZRedisLive(private val rs: Redis) extends ZRedisService {
......
...@@ -25,39 +25,38 @@ import zio.redis.RedisError ...@@ -25,39 +25,38 @@ import zio.redis.RedisError
import zio.schema.Schema import zio.schema.Schema
import zio.{ IO, Layer, ZIO } import zio.{ IO, Layer, ZIO }
/** /** Redis service.
* Redis service.
* *
* @author 梦境迷离 * @author
* @version 2.0,2022/1/10 * 梦境迷离
* @version 2.0,2022/1/10
*/ */
trait ZRedisService { trait ZRedisService {
/** /** @param key
* @param key * @return
* @return Long * Long
*/ */
def del(key: String): ZIO[ZRedisCacheService, RedisError, Long] def del(key: String): ZIO[ZRedisCacheService, RedisError, Long]
/** /** @param key
* @param key * @param field
* @param field * @param value
* @param value * @return
* @return Long * Long
*/ */
def hSet[T: Schema](key: String, field: String, value: T): ZIO[ZRedisCacheService, RedisError, Long] def hSet[T: Schema](key: String, field: String, value: T): ZIO[ZRedisCacheService, RedisError, Long]
/** /** @param key
* @param key * @param field
* @param field * @return
* @return Option[T] * Option[T]
*/ */
def hGet[T: Schema](key: String, field: String): ZIO[ZRedisCacheService, RedisError, Option[T]] def hGet[T: Schema](key: String, field: String): ZIO[ZRedisCacheService, RedisError, Option[T]]
/** /** @param key
* @param key * @tparam T
* @tparam T * @return
* @return
*/ */
def hGetAll[T: Schema](key: String): ZIO[ZRedisCacheService, RedisError, Map[String, T]] def hGetAll[T: Schema](key: String): ZIO[ZRedisCacheService, RedisError, Map[String, T]]
......
...@@ -23,10 +23,10 @@ package org.bitlap.cacheable ...@@ -23,10 +23,10 @@ package org.bitlap.cacheable
import zio.Has import zio.Has
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2022/2/27 * @since 2022/2/27
* @version 1.0 * @version 1.0
*/ */
package object redis { package object redis {
......
...@@ -29,10 +29,10 @@ import zio.stream.ZStream ...@@ -29,10 +29,10 @@ import zio.stream.ZStream
import scala.util.Random import scala.util.Random
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2022/3/19 * @since 2022/3/19
* @version 1.0 * @version 1.0
*/ */
class CacheEvictTest extends AnyFlatSpec with Matchers { class CacheEvictTest extends AnyFlatSpec with Matchers {
...@@ -45,7 +45,7 @@ class CacheEvictTest extends AnyFlatSpec with Matchers { ...@@ -45,7 +45,7 @@ class CacheEvictTest extends AnyFlatSpec with Matchers {
def readIOFunction: String = "hello world" def readIOFunction: String = "hello world"
val readIOMethodName = "readIOFunction" val readIOMethodName = "readIOFunction"
val readStreamMethodName = "readStreamFunction" val readStreamMethodName = "readStreamFunction"
"cacheEvict1" should "ok" in { "cacheEvict1" should "ok" in {
......
...@@ -30,14 +30,14 @@ import zio.Chunk ...@@ -30,14 +30,14 @@ import zio.Chunk
import scala.util.Random import scala.util.Random
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2021/8/7 * @since 2021/8/7
* @version 1.0 * @version 1.0
*/ */
class CacheableTest extends AnyFlatSpec with Matchers { class CacheableTest extends AnyFlatSpec with Matchers {
val runtime = zio.Runtime.default val runtime = zio.Runtime.default
val readIOMethodName = "readIOFunction" val readIOMethodName = "readIOFunction"
"cacheable1" should "ok" in { "cacheable1" should "ok" in {
...@@ -67,9 +67,9 @@ class CacheableTest extends AnyFlatSpec with Matchers { ...@@ -67,9 +67,9 @@ class CacheableTest extends AnyFlatSpec with Matchers {
println(chunk) println(chunk)
val result = runtime.unsafeRun(for { val result = runtime.unsafeRun(for {
_ <- ZRedisService.del("CacheableTest-readStreamFunction") _ <- ZRedisService.del("CacheableTest-readStreamFunction")
method <- readStreamFunction(1, "hello").runCollect method <- readStreamFunction(1, "hello").runCollect
cache <- ZRedisService.hGet[Chunk[String]]("CacheableTest-readStreamFunction", "1-hello") cache <- ZRedisService.hGet[Chunk[String]]("CacheableTest-readStreamFunction", "1-hello")
} yield method -> cache.getOrElse(Chunk.empty)) } yield method -> cache.getOrElse(Chunk.empty))
result._1 shouldEqual result._2 result._1 shouldEqual result._2
} }
...@@ -82,7 +82,7 @@ class CacheableTest extends AnyFlatSpec with Matchers { ...@@ -82,7 +82,7 @@ class CacheableTest extends AnyFlatSpec with Matchers {
ZStream.fromEffect(ZIO.effect(cacheValue)) ZStream.fromEffect(ZIO.effect(cacheValue))
val result = runtime.unsafeRun(for { val result = runtime.unsafeRun(for {
_ <- ZRedisService.del("CacheableTest-readEntityStreamFunction") _ <- ZRedisService.del("CacheableTest-readEntityStreamFunction")
method <- readEntityStreamFunction(1, "hello").runHead method <- readEntityStreamFunction(1, "hello").runHead
} yield method) } yield method)
...@@ -97,9 +97,9 @@ class CacheableTest extends AnyFlatSpec with Matchers { ...@@ -97,9 +97,9 @@ class CacheableTest extends AnyFlatSpec with Matchers {
ZIO.effect(cacheValue) ZIO.effect(cacheValue)
val result = runtime.unsafeRun(for { val result = runtime.unsafeRun(for {
_ <- ZRedisService.del("CacheableTest-readEntityIOFunction") _ <- ZRedisService.del("CacheableTest-readEntityIOFunction")
method <- readEntityIOFunction(1, "hello") method <- readEntityIOFunction(1, "hello")
cache <- ZRedisService.hGet[CacheValue]("CacheableTest-readEntityIOFunction", "1-hello") cache <- ZRedisService.hGet[CacheValue]("CacheableTest-readEntityIOFunction", "1-hello")
} yield Some(method) -> cache) } yield Some(method) -> cache)
result._1 shouldEqual result._2 result._1 shouldEqual result._2
......
...@@ -30,10 +30,10 @@ import zio.schema.{ Schema, StandardType } ...@@ -30,10 +30,10 @@ import zio.schema.{ Schema, StandardType }
import scala.util.Random import scala.util.Random
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2022/3/20 * @since 2022/3/20
* @version 1.0 * @version 1.0
*/ */
class CustomCacheableTest extends AnyFlatSpec with Matchers { class CustomCacheableTest extends AnyFlatSpec with Matchers {
......
...@@ -23,12 +23,12 @@ package org.bitlap.csv.core ...@@ -23,12 +23,12 @@ package org.bitlap.csv.core
import scala.collection.immutable.{ :: => Cons } import scala.collection.immutable.{ :: => Cons }
/** /** Csv encoder and decoder.
* Csv encoder and decoder.
* *
* @author 梦境迷离 * @author
* @since 2022/04/27 * 梦境迷离
* @version 1.0 * @since 2022/04/27
* @version 1.0
*/ */
trait Converter[T] { trait Converter[T] {
...@@ -97,7 +97,7 @@ object Converter { ...@@ -97,7 +97,7 @@ object Converter {
case Nil => Some(Nil) case Nil => Some(Nil)
case Cons(s, ss) => case Cons(s, ss) =>
for { for {
x <- ec.toScala(s) x <- ec.toScala(s)
xs <- listCsvLinesConverter(ss)(ec) xs <- listCsvLinesConverter(ss)(ec)
} yield Cons(x, xs) } yield Cons(x, xs)
} }
......
...@@ -21,20 +21,20 @@ ...@@ -21,20 +21,20 @@
package org.bitlap.csv.core package org.bitlap.csv.core
/** /** a Custom Csv encoder.
* a Custom Csv encoder.
* *
* @author 梦境迷离 * @author
* @since 2022/04/27 * 梦境迷离
* @version 1.0 * @since 2022/04/27
* @version 1.0
*/ */
trait Csvable[T] { trait Csvable[T] {
/** /** API for processing a specific field of case class object.
* API for processing a specific field of case class object.
* *
* @param t case class object * @param t
* @return * case class object
* @return
*/ */
def _toCsvString(t: T): String def _toCsvString(t: T): String
......
...@@ -24,59 +24,63 @@ package org.bitlap.csv.core ...@@ -24,59 +24,63 @@ package org.bitlap.csv.core
import org.bitlap.csv.core.macros.DeriveCsvableBuilder import org.bitlap.csv.core.macros.DeriveCsvableBuilder
import java.io.File import java.io.File
/** /** Builder to create a custom Csv Encoder.
* Builder to create a custom Csv Encoder.
* *
* @author 梦境迷离 * @author
* @version 1.0,2022/4/30 * 梦境迷离
* @version 1.0,2022/4/30
*/ */
class CsvableBuilder[T] { class CsvableBuilder[T] {
/** /** Convert this CSV column string to any Scala types.
* Convert this CSV column string to any Scala types.
* *
* @param scalaField The field in scala case class. * @param scalaField
* @param value This function specifies how you want to convert this field to a CSV string. * The field in scala case class.
* @tparam SF The field type, generally, it is not necessary to specify, but it is safer if specify. * @param value
* @return * This function specifies how you want to convert this field to a CSV string.
* @tparam SF
* The field type, generally, it is not necessary to specify, but it is safer if specify.
* @return
*/ */
def setField[SF](scalaField: T => SF, value: SF => String): CsvableBuilder[T] = def setField[SF](scalaField: T => SF, value: SF => String): CsvableBuilder[T] =
macro DeriveCsvableBuilder.setFieldImpl[T, SF] macro DeriveCsvableBuilder.setFieldImpl[T, SF]
/** /** Create a custom builder for converting this scala value to CSV line string.
* Create a custom builder for converting this scala value to CSV line string.
* *
* @param t The value of Scala case class. * @param t
* @param columnSeparator The separator for CSV column value. * The value of Scala case class.
* @return * @param columnSeparator
* The separator for CSV column value.
* @return
*/ */
def convert(t: T, columnSeparator: Char): String = macro DeriveCsvableBuilder.convertOneImpl[T] def convert(t: T, columnSeparator: Char): String = macro DeriveCsvableBuilder.convertOneImpl[T]
/** /** Make columnSeparator assign to `,` as default value.
* Make columnSeparator assign to `,` as default value.
*/ */
def convert(t: T): String = macro DeriveCsvableBuilder.convertOneDefaultImpl[T] def convert(t: T): String = macro DeriveCsvableBuilder.convertOneDefaultImpl[T]
/** /** Convert the sequence of Scala case class to CSV string.
* Convert the sequence of Scala case class to CSV string.
* *
* @param ts The sequence of Scala case class. * @param ts
* @param columnSeparator The separator for CSV column value. * The sequence of Scala case class.
* @return It combines CSV lines by '\n'. * @param columnSeparator
* The separator for CSV column value.
* @return
* It combines CSV lines by '\n'.
*/ */
def convert(ts: List[T], columnSeparator: Char): String = macro DeriveCsvableBuilder.convertImpl[T] def convert(ts: List[T], columnSeparator: Char): String = macro DeriveCsvableBuilder.convertImpl[T]
/** /** Make columnSeparator assign to `,` as default value.
* Make columnSeparator assign to `,` as default value.
*/ */
def convert(ts: List[T]): String = macro DeriveCsvableBuilder.convertDefaultImpl[T] def convert(ts: List[T]): String = macro DeriveCsvableBuilder.convertDefaultImpl[T]
/** /** Convert the sequence of Scala case class to CSV string and write to file.
* Convert the sequence of Scala case class to CSV string and write to file.
* *
* @param ts The sequence of Scala case class. * @param ts
* @param file File to save CSV string. * The sequence of Scala case class.
* @return * @param file
* File to save CSV string.
* @return
*/ */
def convertTo(ts: List[T], file: File): Boolean = macro DeriveCsvableBuilder.convertToFileImpl[T] def convertTo(ts: List[T], file: File): Boolean = macro DeriveCsvableBuilder.convertToFileImpl[T]
......
...@@ -21,9 +21,9 @@ ...@@ -21,9 +21,9 @@
package org.bitlap.csv.core package org.bitlap.csv.core
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/5/1 * @version 1.0,2022/5/1
*/ */
trait CsvableImplicits { trait CsvableImplicits {
......
...@@ -28,9 +28,9 @@ import scala.util.control.Exception.ignoring ...@@ -28,9 +28,9 @@ import scala.util.control.Exception.ignoring
import scala.collection.mutable.ListBuffer import scala.collection.mutable.ListBuffer
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,5/13/22 * @version 1.0,5/13/22
*/ */
object FileUtils { object FileUtils {
...@@ -40,23 +40,28 @@ object FileUtils { ...@@ -40,23 +40,28 @@ object FileUtils {
def using[R <: Closable, T](resource: => R)(f: R => T): T = def using[R <: Closable, T](resource: => R)(f: R => T): T =
try f(resource) try f(resource)
finally ignoring(classOf[Throwable]) apply { finally
resource.close() ignoring(classOf[Throwable]) apply {
} resource.close()
}
def writer(file: File, lines: List[String]): Boolean = { def writer(file: File, lines: List[String]): Boolean = {
checkFile(file) checkFile(file)
val bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file)) val bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file))
try using(new PrintWriter(bufferedOutputStream, true)) { r => try
lines.foreach(r.println) using(new PrintWriter(bufferedOutputStream, true)) { r =>
} finally bufferedOutputStream.close() lines.foreach(r.println)
}
finally bufferedOutputStream.close()
true true
} }
def reader(file: InputStream, charset: String = "UTF-8"): List[String] = def reader(file: InputStream, charset: String = "UTF-8"): List[String] =
try using(Source.fromInputStream(new BufferedInputStream(file), charset)) { lines => try
lines.getLines().toList using(Source.fromInputStream(new BufferedInputStream(file), charset)) { lines =>
} finally file.close() lines.getLines().toList
}
finally file.close()
def checkFile(file: File): Unit = { def checkFile(file: File): Unit = {
if (file.isDirectory) { if (file.isDirectory) {
...@@ -68,7 +73,7 @@ object FileUtils { ...@@ -68,7 +73,7 @@ object FileUtils {
} }
def readFileFunc[T](reader: BufferedReader, func: String => Option[T]): List[Option[T]] = { def readFileFunc[T](reader: BufferedReader, func: String => Option[T]): List[Option[T]] = {
val ts = ListBuffer[Option[T]]() val ts = ListBuffer[Option[T]]()
var line: String = null var line: String = null
FileUtils.using(new BufferedReader(reader)) { input => FileUtils.using(new BufferedReader(reader)) { input =>
while ({ while ({
......
...@@ -21,20 +21,20 @@ ...@@ -21,20 +21,20 @@
package org.bitlap.csv.core package org.bitlap.csv.core
/** /** a Custom Csv decoder.
* a Custom Csv decoder.
* *
* @author 梦境迷离 * @author
* @since 2022/04/30 * 梦境迷离
* @version 1.0 * @since 2022/04/30
* @version 1.0
*/ */
trait Scalable[T] { trait Scalable[T] {
/** /** API for processing a specific column value of CSV line data.
* API for processing a specific column value of CSV line data.
* *
* @param column The column value of CSV line data. * @param column
* @return * The column value of CSV line data.
* @return
*/ */
def _toScala(column: String): Option[T] def _toScala(column: String): Option[T]
} }
......
...@@ -24,59 +24,62 @@ package org.bitlap.csv.core ...@@ -24,59 +24,62 @@ package org.bitlap.csv.core
import org.bitlap.csv.core.macros.DeriveScalableBuilder import org.bitlap.csv.core.macros.DeriveScalableBuilder
import java.io.InputStream import java.io.InputStream
/** /** Builder to create a custom Csv Decoder.
* Builder to create a custom Csv Decoder.
* *
* @author 梦境迷离 * @author
* @version 1.0,2022/4/30 * 梦境迷离
* @version 1.0,2022/4/30
*/ */
class ScalableBuilder[T] { class ScalableBuilder[T] {
/** /** Convert any Scala types to this CSV column string.
* Convert any Scala types to this CSV column string.
* *
* @param scalaField The field in scala case class. * @param scalaField
* @param value This function specifies how you want to convert this CSV column to a scala type. * The field in scala case class.
* @tparam SF The field type, generally, it is not necessary to specify, but it is safer if specify. * @param value
* @return * This function specifies how you want to convert this CSV column to a scala type.
* @tparam SF
* The field type, generally, it is not necessary to specify, but it is safer if specify.
* @return
*/ */
def setField[SF](scalaField: T => SF, value: String => SF): ScalableBuilder[T] = def setField[SF](scalaField: T => SF, value: String => SF): ScalableBuilder[T] =
macro DeriveScalableBuilder.setFieldImpl[T, SF] macro DeriveScalableBuilder.setFieldImpl[T, SF]
/** /** Create a custom builder for converting this CSV line to scala values.
* Create a custom builder for converting this CSV line to scala values.
* *
* @param line One CSV line. * @param line
* @param columnSeparator The separator for CSV column value. * One CSV line.
* @return * @param columnSeparator
* The separator for CSV column value.
* @return
*/ */
def convert(line: String, columnSeparator: Char): Option[T] = macro DeriveScalableBuilder.convertOneImpl[T] def convert(line: String, columnSeparator: Char): Option[T] = macro DeriveScalableBuilder.convertOneImpl[T]
/** /** Make columnSeparator assign to `,` as default value.
* Make columnSeparator assign to `,` as default value.
*/ */
def convert(line: String): Option[T] = macro DeriveScalableBuilder.convertOneDefaultImpl[T] def convert(line: String): Option[T] = macro DeriveScalableBuilder.convertOneDefaultImpl[T]
/** /** Convert all CSV lines to the sequence of Scala case class.
* Convert all CSV lines to the sequence of Scala case class.
* *
* @param lines All CSV lines. * @param lines
* @param columnSeparator The separator for CSV column value. * All CSV lines.
* @return * @param columnSeparator
* The separator for CSV column value.
* @return
*/ */
def convert(lines: List[String], columnSeparator: Char): List[Option[T]] = macro DeriveScalableBuilder.convertImpl[T] def convert(lines: List[String], columnSeparator: Char): List[Option[T]] = macro DeriveScalableBuilder.convertImpl[T]
/** /** Make columnSeparator assign to `,` as default value.
* Make columnSeparator assign to `,` as default value.
*/ */
def convert(lines: List[String]): List[Option[T]] = macro DeriveScalableBuilder.convertDefaultImpl[T] def convert(lines: List[String]): List[Option[T]] = macro DeriveScalableBuilder.convertDefaultImpl[T]
/** /** Read all CSV lines of the file and convert them to the sequence of Scala case class.
* Read all CSV lines of the file and convert them to the sequence of Scala case class.
* *
* @param file InputStream of the CSV file. * @param file
* @param charset String charset of the CSV file content. * InputStream of the CSV file.
* @return * @param charset
* String charset of the CSV file content.
* @return
*/ */
def convertFrom(file: InputStream, charset: String): List[Option[T]] = def convertFrom(file: InputStream, charset: String): List[Option[T]] =
macro DeriveScalableBuilder.convertFromFileImpl[T] macro DeriveScalableBuilder.convertFromFileImpl[T]
......
...@@ -23,11 +23,11 @@ package org.bitlap.csv.core ...@@ -23,11 +23,11 @@ package org.bitlap.csv.core
import java.io.{ BufferedReader, File, FileReader, InputStreamReader } import java.io.{ BufferedReader, File, FileReader, InputStreamReader }
/** /** Tool class for parsing CSV files.
* Tool class for parsing CSV files.
* *
* @author 梦境迷离 * @author
* @version 1.0,2022/5/13 * 梦境迷离
* @version 1.0,2022/5/13
*/ */
object ScalableHelper { object ScalableHelper {
......
...@@ -21,9 +21,9 @@ ...@@ -21,9 +21,9 @@
package org.bitlap.csv.core package org.bitlap.csv.core
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/5/1 * @version 1.0,2022/5/1
*/ */
trait ScalableImplicits { trait ScalableImplicits {
......
...@@ -25,16 +25,16 @@ import java.util.regex.Pattern ...@@ -25,16 +25,16 @@ import java.util.regex.Pattern
import scala.collection.mutable.ListBuffer import scala.collection.mutable.ListBuffer
import scala.util.matching.Regex import scala.util.matching.Regex
/** /** split csv column value by columnSeparator.
* split csv column value by columnSeparator.
* *
* @author 梦境迷离 * @author
* @version 1.0,2022/4/30 * 梦境迷离
* @version 1.0,2022/4/30
*/ */
object StringUtils { object StringUtils {
private val regex: Regex = "\\{(.*?)\\}".r private val regex: Regex = "\\{(.*?)\\}".r
private val kvr: Regex = "(.*):(.*)".r private val kvr: Regex = "(.*):(.*)".r
private val pattern: Pattern = Pattern.compile(regex.toString()) private val pattern: Pattern = Pattern.compile(regex.toString())
def extraJsonPairs(input: String): String = { def extraJsonPairs(input: String): String = {
...@@ -60,9 +60,9 @@ object StringUtils { ...@@ -60,9 +60,9 @@ object StringUtils {
} }
def splitColumns(line: => String, columnSeparator: Char): List[String] = { def splitColumns(line: => String, columnSeparator: Char): List[String] = {
val listBuffer = ListBuffer[String]() val listBuffer = ListBuffer[String]()
val columnBuffer = ListBuffer[Char]() val columnBuffer = ListBuffer[Char]()
val chars = line.toCharArray val chars = line.toCharArray
var idx = 0 var idx = 0
while (idx < chars.length) while (idx < chars.length)
......
...@@ -25,12 +25,12 @@ import java.time.ZonedDateTime ...@@ -25,12 +25,12 @@ import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import scala.reflect.macros.blackbox import scala.reflect.macros.blackbox
/** /** This is a generic implementation of macro handling, and subclasses need to inherit it to reduce redundant code.
* This is a generic implementation of macro handling, and subclasses need to inherit it to reduce redundant code.
* *
* @author 梦境迷离 * @author
* @since 2021/7/24 * 梦境迷离
* @version 1.0 * @since 2021/7/24
* @version 1.0
*/ */
abstract class AbstractMacroProcessor(val c: blackbox.Context) { abstract class AbstractMacroProcessor(val c: blackbox.Context) {
...@@ -38,20 +38,22 @@ abstract class AbstractMacroProcessor(val c: blackbox.Context) { ...@@ -38,20 +38,22 @@ abstract class AbstractMacroProcessor(val c: blackbox.Context) {
protected val packageName = q"_root_.org.bitlap.csv.core" protected val packageName = q"_root_.org.bitlap.csv.core"
/** /** Get the list of case class constructor parameters and return the column index, column name, and parameter type
* Get the list of case class constructor parameters and return the column index, column name, and parameter type that zip as a `List[((Int, Tree), Type)]`. * that zip as a `List[((Int, Tree), Type)]`.
* *
* @param columnsFunc The function to get CSV row data temporary identifier, also known as a line. * @param columnsFunc
* @tparam T Type of the case class. * The function to get CSV row data temporary identifier, also known as a line.
* @return * @tparam T
* Type of the case class.
* @return
*/ */
private[macros] def checkCaseClassZipAll[T: c.WeakTypeTag]( private[macros] def checkCaseClassZipAll[T: c.WeakTypeTag](
columnsFunc: TermName columnsFunc: TermName
): List[((Int, Tree), Type)] = { ): List[((Int, Tree), Type)] = {
val idxColumn = (i: Int) => q"$columnsFunc()($i)" val idxColumn = (i: Int) => q"$columnsFunc()($i)"
val params = getCaseClassParams[T]() val params = getCaseClassParams[T]()
val paramsSize = params.size val paramsSize = params.size
val types = params.map(f => c.typecheck(tq"$f", c.TYPEmode).tpe) val types = params.map(f => c.typecheck(tq"$f", c.TYPEmode).tpe)
val indexColumns = (0 until paramsSize).toList.map(i => i -> idxColumn(i)) val indexColumns = (0 until paramsSize).toList.map(i => i -> idxColumn(i))
if (indexColumns.size != types.size) { if (indexColumns.size != types.size) {
c.abort(c.enclosingPosition, "The column num of CSV file is different from that in case class constructor!") c.abort(c.enclosingPosition, "The column num of CSV file is different from that in case class constructor!")
...@@ -60,11 +62,11 @@ abstract class AbstractMacroProcessor(val c: blackbox.Context) { ...@@ -60,11 +62,11 @@ abstract class AbstractMacroProcessor(val c: blackbox.Context) {
indexColumns zip types indexColumns zip types
} }
/** /** Get only the symbol of the case class constructor parameters.
* Get only the symbol of the case class constructor parameters.
* *
* @tparam T Type of the case class. * @tparam T
* @return * Type of the case class.
* @return
*/ */
private[macros] def getCaseClassParams[T: c.WeakTypeTag](): List[Symbol] = { private[macros] def getCaseClassParams[T: c.WeakTypeTag](): List[Symbol] = {
val parameters = resolveParameters[T] val parameters = resolveParameters[T]
...@@ -74,13 +76,12 @@ abstract class AbstractMacroProcessor(val c: blackbox.Context) { ...@@ -74,13 +76,12 @@ abstract class AbstractMacroProcessor(val c: blackbox.Context) {
parameters.flatten parameters.flatten
} }
/** /** Print the expanded code of macro.
* Print the expanded code of macro.
* *
* @param force * @param force
* @param resTree * @param resTree
* @tparam T * @tparam T
* @return * @return
*/ */
def exprPrintTree[T: c.WeakTypeTag](force: Boolean, resTree: c.Tree): c.Expr[T] = { def exprPrintTree[T: c.WeakTypeTag](force: Boolean, resTree: c.Tree): c.Expr[T] = {
c.info( c.info(
...@@ -92,42 +93,43 @@ abstract class AbstractMacroProcessor(val c: blackbox.Context) { ...@@ -92,42 +93,43 @@ abstract class AbstractMacroProcessor(val c: blackbox.Context) {
c.Expr[T](resTree) c.Expr[T](resTree)
} }
/** /** Get the constructor symbol of the case class.
* Get the constructor symbol of the case class.
* *
* @tparam T Type of the case class. * @tparam T
* @return The parameters may be currying, so it's a two-level list. * Type of the case class.
* @return
* The parameters may be currying, so it's a two-level list.
*/ */
private[macros] def resolveParameters[T: c.WeakTypeTag]: List[List[Symbol]] = private[macros] def resolveParameters[T: c.WeakTypeTag]: List[List[Symbol]] =
c.weakTypeOf[T].resultType.member(TermName("<init>")).typeSignature.paramLists c.weakTypeOf[T].resultType.member(TermName("<init>")).typeSignature.paramLists
/** /** Get the `TypeName` of the class.
* Get the `TypeName` of the class.
* *
* @tparam T Type of the case class. * @tparam T
* @return * Type of the case class.
* @return
*/ */
private[macros] def resolveClazzTypeName[T: c.WeakTypeTag]: c.universe.TypeName = private[macros] def resolveClazzTypeName[T: c.WeakTypeTag]: c.universe.TypeName =
TypeName(c.weakTypeOf[T].typeSymbol.name.decodedName.toString) TypeName(c.weakTypeOf[T].typeSymbol.name.decodedName.toString)
/** /** Get the list of case class constructor parameters and return the column index and parameter type that zip as a
* Get the list of case class constructor parameters and return the column index and parameter type that zip as a `List[(Int, Type)])`. * `List[(Int, Type)])`.
* *
* @tparam T Type of the case class. * @tparam T
* @return * Type of the case class.
* @return
*/ */
private[macros] def checkCaseClassZip[T: c.WeakTypeTag]: (List[String], List[(Int, Type)]) = { private[macros] def checkCaseClassZip[T: c.WeakTypeTag]: (List[String], List[(Int, Type)]) = {
val params = getCaseClassParams[T]() val params = getCaseClassParams[T]()
val paramsSize = params.size val paramsSize = params.size
val names = params.map(p => p.name.decodedName.toString) val names = params.map(p => p.name.decodedName.toString)
names -> params.zip(0 until paramsSize).map(f => f._2 -> c.typecheck(tq"${f._1}", c.TYPEmode).tpe) names -> params.zip(0 until paramsSize).map(f => f._2 -> c.typecheck(tq"${f._1}", c.TYPEmode).tpe)
} }
/** /** Get the builderId of the current class which generated by *Builder,apply macro.
* Get the builderId of the current class which generated by *Builder,apply macro.
* *
* @param annoBuilderPrefix * @param annoBuilderPrefix
* @return * @return
*/ */
private[macros] def getBuilderId(annoBuilderPrefix: String): Int = private[macros] def getBuilderId(annoBuilderPrefix: String): Int =
c.prefix.actualType.toString.replace(annoBuilderPrefix, "").toInt c.prefix.actualType.toString.replace(annoBuilderPrefix, "").toInt
......
...@@ -27,9 +27,9 @@ import scala.collection.mutable ...@@ -27,9 +27,9 @@ import scala.collection.mutable
import scala.reflect.macros.whitebox import scala.reflect.macros.whitebox
import java.io.File import java.io.File
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/4/29 * @version 1.0,2022/4/29
*/ */
class DeriveCsvableBuilder(override val c: whitebox.Context) extends AbstractMacroProcessor(c) { class DeriveCsvableBuilder(override val c: whitebox.Context) extends AbstractMacroProcessor(c) {
...@@ -39,16 +39,16 @@ class DeriveCsvableBuilder(override val c: whitebox.Context) extends AbstractMac ...@@ -39,16 +39,16 @@ class DeriveCsvableBuilder(override val c: whitebox.Context) extends AbstractMac
private val builderFunctionPrefix = "_CsvableBuilderFunction$" private val builderFunctionPrefix = "_CsvableBuilderFunction$"
private val innerTName = q"_t" private val innerTName = q"_t"
private val innerTmpTermName = TermName("_tt") private val innerTmpTermName = TermName("_tt")
private val csvableInstanceTermName = TermName("_csvableInstance") private val csvableInstanceTermName = TermName("_csvableInstance")
private val csvableImplClassNamePrefix = "_CsvAnno$" private val csvableImplClassNamePrefix = "_CsvAnno$"
private val funcArgsTempTermName = TermName("temp") private val funcArgsTempTermName = TermName("temp")
// scalafmt: { maxColumn = 400 } // scalafmt: { maxColumn = 400 }
def setFieldImpl[T: WeakTypeTag, SF: WeakTypeTag](scalaField: Expr[T => SF], value: Expr[SF => String]): Expr[CsvableBuilder[T]] = { def setFieldImpl[T: WeakTypeTag, SF: WeakTypeTag](scalaField: Expr[T => SF], value: Expr[SF => String]): Expr[CsvableBuilder[T]] = {
val Function(_, Select(_, termName)) = scalaField.tree val Function(_, Select(_, termName)) = scalaField.tree
val builderId = getBuilderId(annoBuilderPrefix) val builderId = getBuilderId(annoBuilderPrefix)
MacroCache.builderFunctionTrees.getOrElseUpdate(builderId, mutable.Map.empty).update(termName.toString, value) MacroCache.builderFunctionTrees.getOrElseUpdate(builderId, mutable.Map.empty).update(termName.toString, value)
val tree = q"new ${c.prefix.actualType}" val tree = q"new ${c.prefix.actualType}"
exprPrintTree[CsvableBuilder[T]](force = false, tree) exprPrintTree[CsvableBuilder[T]](force = false, tree)
...@@ -73,7 +73,7 @@ class DeriveCsvableBuilder(override val c: whitebox.Context) extends AbstractMac ...@@ -73,7 +73,7 @@ class DeriveCsvableBuilder(override val c: whitebox.Context) extends AbstractMac
deriveFullIntoFileCsvableImpl[T](ts, file, c.Expr[Char](q"','")) deriveFullIntoFileCsvableImpl[T](ts, file, c.Expr[Char](q"','"))
private def deriveBuilderApplyImpl[T: WeakTypeTag]: Expr[CsvableBuilder[T]] = { private def deriveBuilderApplyImpl[T: WeakTypeTag]: Expr[CsvableBuilder[T]] = {
val className = TypeName(annoBuilderPrefix + MacroCache.getBuilderId) val className = TypeName(annoBuilderPrefix + MacroCache.getBuilderId)
val caseClazzName = TypeName(weakTypeOf[T].typeSymbol.name.decodedName.toString) val caseClazzName = TypeName(weakTypeOf[T].typeSymbol.name.decodedName.toString)
val tree = val tree =
q""" q"""
...@@ -97,7 +97,7 @@ class DeriveCsvableBuilder(override val c: whitebox.Context) extends AbstractMac ...@@ -97,7 +97,7 @@ class DeriveCsvableBuilder(override val c: whitebox.Context) extends AbstractMac
// scalafmt: { maxColumn = 400 } // scalafmt: { maxColumn = 400 }
private def deriveFullIntoFileCsvableImpl[T: WeakTypeTag](ts: Expr[List[T]], file: Expr[File], columnSeparator: Expr[Char]): Expr[Boolean] = { private def deriveFullIntoFileCsvableImpl[T: WeakTypeTag](ts: Expr[List[T]], file: Expr[File], columnSeparator: Expr[Char]): Expr[Boolean] = {
val clazzName = resolveClazzTypeName[T] val clazzName = resolveClazzTypeName[T]
val (customTrees, preTrees) = getCustomPreTress val (customTrees, preTrees) = getCustomPreTress
val tree = q""" val tree = q"""
..$preTrees ..$preTrees
...@@ -113,7 +113,7 @@ class DeriveCsvableBuilder(override val c: whitebox.Context) extends AbstractMac ...@@ -113,7 +113,7 @@ class DeriveCsvableBuilder(override val c: whitebox.Context) extends AbstractMac
// scalafmt: { maxColumn = 400 } // scalafmt: { maxColumn = 400 }
private def deriveFullCsvableImpl[T: WeakTypeTag](ts: Expr[List[T]], columnSeparator: Expr[Char]): Expr[String] = { private def deriveFullCsvableImpl[T: WeakTypeTag](ts: Expr[List[T]], columnSeparator: Expr[Char]): Expr[String] = {
val clazzName = resolveClazzTypeName[T] val clazzName = resolveClazzTypeName[T]
val (customTrees, preTrees) = getCustomPreTress val (customTrees, preTrees) = getCustomPreTress
val tree = val tree =
q""" q"""
...@@ -128,9 +128,9 @@ class DeriveCsvableBuilder(override val c: whitebox.Context) extends AbstractMac ...@@ -128,9 +128,9 @@ class DeriveCsvableBuilder(override val c: whitebox.Context) extends AbstractMac
} }
private def getAnnoClassObject[T: WeakTypeTag](customTrees: mutable.Map[String, Any], columnSeparator: Expr[Char]): Tree = { private def getAnnoClassObject[T: WeakTypeTag](customTrees: mutable.Map[String, Any], columnSeparator: Expr[Char]): Tree = {
val clazzName = resolveClazzTypeName[T] val clazzName = resolveClazzTypeName[T]
val annoClassName = TermName(csvableImplClassNamePrefix + MacroCache.getIdentityId) val annoClassName = TermName(csvableImplClassNamePrefix + MacroCache.getIdentityId)
val separator = q"$columnSeparator" val separator = q"$columnSeparator"
q""" q"""
object $annoClassName extends $packageName.Csvable[$clazzName] { object $annoClassName extends $packageName.Csvable[$clazzName] {
var $innerTmpTermName: $clazzName = _ var $innerTmpTermName: $clazzName = _
...@@ -147,10 +147,10 @@ class DeriveCsvableBuilder(override val c: whitebox.Context) extends AbstractMac ...@@ -147,10 +147,10 @@ class DeriveCsvableBuilder(override val c: whitebox.Context) extends AbstractMac
} }
private def deriveCsvableImpl[T: WeakTypeTag](t: Expr[T], columnSeparator: Expr[Char]): Expr[String] = { private def deriveCsvableImpl[T: WeakTypeTag](t: Expr[T], columnSeparator: Expr[Char]): Expr[String] = {
val clazzName = resolveClazzTypeName[T] val clazzName = resolveClazzTypeName[T]
val (customTrees, preTrees) = getCustomPreTress val (customTrees, preTrees) = getCustomPreTress
val annoClassName = TermName(csvableImplClassNamePrefix + MacroCache.getIdentityId) val annoClassName = TermName(csvableImplClassNamePrefix + MacroCache.getIdentityId)
val separator = q"$columnSeparator" val separator = q"$columnSeparator"
val tree = val tree =
q""" q"""
...@@ -170,9 +170,9 @@ class DeriveCsvableBuilder(override val c: whitebox.Context) extends AbstractMac ...@@ -170,9 +170,9 @@ class DeriveCsvableBuilder(override val c: whitebox.Context) extends AbstractMac
// scalafmt: { maxColumn = 400 } // scalafmt: { maxColumn = 400 }
private def fieldsToString[T: WeakTypeTag](innerVarTermName: TermName, customTrees: mutable.Map[String, Any]): List[Tree] = { private def fieldsToString[T: WeakTypeTag](innerVarTermName: TermName, customTrees: mutable.Map[String, Any]): List[Tree] = {
val clazzName = resolveClazzTypeName[T] val clazzName = resolveClazzTypeName[T]
val (fieldNames, indexTypes) = checkCaseClassZip val (fieldNames, indexTypes) = checkCaseClassZip
val indexByName = (i: Int) => TermName(fieldNames(i)) val indexByName = (i: Int) => TermName(fieldNames(i))
indexTypes.map { idxType => indexTypes.map { idxType =>
val customFunction = () => q"${TermName(builderFunctionPrefix + fieldNames(idxType._1))}.apply($innerVarTermName.${indexByName(idxType._1)})" val customFunction = () => q"${TermName(builderFunctionPrefix + fieldNames(idxType._1))}.apply($innerVarTermName.${indexByName(idxType._1)})"
idxType._2 match { idxType._2 match {
......
...@@ -27,9 +27,9 @@ import java.io.InputStream ...@@ -27,9 +27,9 @@ import java.io.InputStream
import scala.collection.mutable import scala.collection.mutable
import scala.reflect.macros.whitebox import scala.reflect.macros.whitebox
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/4/29 * @version 1.0,2022/4/29
*/ */
class DeriveScalableBuilder(override val c: whitebox.Context) extends AbstractMacroProcessor(c) { class DeriveScalableBuilder(override val c: whitebox.Context) extends AbstractMacroProcessor(c) {
...@@ -39,16 +39,16 @@ class DeriveScalableBuilder(override val c: whitebox.Context) extends AbstractMa ...@@ -39,16 +39,16 @@ class DeriveScalableBuilder(override val c: whitebox.Context) extends AbstractMa
private val builderFunctionPrefix = "_ScalableBuilderFunction$" private val builderFunctionPrefix = "_ScalableBuilderFunction$"
private val innerColumnFuncTermName = TermName("_columns") private val innerColumnFuncTermName = TermName("_columns")
private val innerLName = q"_l" private val innerLName = q"_l"
private val innerTempTermName = TermName("_line") private val innerTempTermName = TermName("_line")
private val scalableInstanceTermName = TermName("_scalableInstance") private val scalableInstanceTermName = TermName("_scalableInstance")
private val scalableImplClassNamePrefix = "_ScalaAnno$" private val scalableImplClassNamePrefix = "_ScalaAnno$"
// scalafmt: { maxColumn = 400 } // scalafmt: { maxColumn = 400 }
def setFieldImpl[T: WeakTypeTag, SF: WeakTypeTag](scalaField: Expr[T => SF], value: Expr[String => SF]): Expr[ScalableBuilder[T]] = { def setFieldImpl[T: WeakTypeTag, SF: WeakTypeTag](scalaField: Expr[T => SF], value: Expr[String => SF]): Expr[ScalableBuilder[T]] = {
val Function(_, Select(_, termName)) = scalaField.tree val Function(_, Select(_, termName)) = scalaField.tree
val builderId = getBuilderId(annoBuilderPrefix) val builderId = getBuilderId(annoBuilderPrefix)
MacroCache.builderFunctionTrees.getOrElseUpdate(builderId, mutable.Map.empty).update(termName.toString, value) MacroCache.builderFunctionTrees.getOrElseUpdate(builderId, mutable.Map.empty).update(termName.toString, value)
val tree = q"new ${c.prefix.actualType}" val tree = q"new ${c.prefix.actualType}"
exprPrintTree[ScalableBuilder[T]](force = false, tree) exprPrintTree[ScalableBuilder[T]](force = false, tree)
...@@ -83,7 +83,7 @@ class DeriveScalableBuilder(override val c: whitebox.Context) extends AbstractMa ...@@ -83,7 +83,7 @@ class DeriveScalableBuilder(override val c: whitebox.Context) extends AbstractMa
} }
private def deriveBuilderApplyImpl[T: WeakTypeTag]: Expr[ScalableBuilder[T]] = { private def deriveBuilderApplyImpl[T: WeakTypeTag]: Expr[ScalableBuilder[T]] = {
val className = TypeName(annoBuilderPrefix + MacroCache.getBuilderId) val className = TypeName(annoBuilderPrefix + MacroCache.getBuilderId)
val caseClazzName = TypeName(weakTypeOf[T].typeSymbol.name.decodedName.toString) val caseClazzName = TypeName(weakTypeOf[T].typeSymbol.name.decodedName.toString)
val tree = val tree =
q""" q"""
...@@ -166,12 +166,12 @@ class DeriveScalableBuilder(override val c: whitebox.Context) extends AbstractMa ...@@ -166,12 +166,12 @@ class DeriveScalableBuilder(override val c: whitebox.Context) extends AbstractMa
// scalafmt: { maxColumn = 400 } // scalafmt: { maxColumn = 400 }
private def scalableBody[T: WeakTypeTag](clazzName: TypeName, innerFuncTermName: TermName): Tree = { private def scalableBody[T: WeakTypeTag](clazzName: TypeName, innerFuncTermName: TermName): Tree = {
val customTrees = MacroCache.builderFunctionTrees.getOrElse(getBuilderId(annoBuilderPrefix), mutable.Map.empty) val customTrees = MacroCache.builderFunctionTrees.getOrElse(getBuilderId(annoBuilderPrefix), mutable.Map.empty)
val params = getCaseClassParams[T]() val params = getCaseClassParams[T]()
val fieldNames = params.map(_.name.decodedName.toString) val fieldNames = params.map(_.name.decodedName.toString)
val fields = checkCaseClassZipAll[T](innerFuncTermName).map { idxType => val fields = checkCaseClassZipAll[T](innerFuncTermName).map { idxType =>
val idx = idxType._1._1 val idx = idxType._1._1
val columnValues = idxType._1._2 val columnValues = idxType._1._2
val fieldTypeName = TypeName(idxType._2.typeSymbol.name.decodedName.toString) val fieldTypeName = TypeName(idxType._2.typeSymbol.name.decodedName.toString)
val customFunction = () => q"${TermName(builderFunctionPrefix + fieldNames(idx))}.apply($columnValues)" val customFunction = () => q"${TermName(builderFunctionPrefix + fieldNames(idx))}.apply($columnValues)"
idxType._2 match { idxType._2 match {
case tp if tp <:< typeOf[List[_]] => case tp if tp <:< typeOf[List[_]] =>
......
...@@ -23,9 +23,9 @@ package org.bitlap.csv.core.macros ...@@ -23,9 +23,9 @@ package org.bitlap.csv.core.macros
import scala.reflect.macros.blackbox import scala.reflect.macros.blackbox
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/4/29 * @version 1.0,2022/4/29
*/ */
object DeriveToCaseClass { object DeriveToCaseClass {
...@@ -37,7 +37,7 @@ object DeriveToCaseClass { ...@@ -37,7 +37,7 @@ object DeriveToCaseClass {
// scalafmt: { maxColumn = 400 } // scalafmt: { maxColumn = 400 }
def macroImpl[T <: Product: c.WeakTypeTag](line: c.Expr[String], columnSeparator: c.Expr[Char]): c.Expr[Option[T]] = { def macroImpl[T <: Product: c.WeakTypeTag](line: c.Expr[String], columnSeparator: c.Expr[Char]): c.Expr[Option[T]] = {
val clazzName = c.weakTypeOf[T].typeSymbol.name val clazzName = c.weakTypeOf[T].typeSymbol.name
val innerFuncTermName = TermName("_columns") val innerFuncTermName = TermName("_columns")
val fields = (columnsFunc: TermName) => val fields = (columnsFunc: TermName) =>
checkCaseClassZipAll[T](columnsFunc).map { idxType => checkCaseClassZipAll[T](columnsFunc).map { idxType =>
......
...@@ -23,9 +23,9 @@ package org.bitlap.csv.core.macros ...@@ -23,9 +23,9 @@ package org.bitlap.csv.core.macros
import scala.reflect.macros.blackbox import scala.reflect.macros.blackbox
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/4/29 * @version 1.0,2022/4/29
*/ */
object DeriveToString { object DeriveToString {
...@@ -37,9 +37,9 @@ object DeriveToString { ...@@ -37,9 +37,9 @@ object DeriveToString {
def macroImpl[T: c.WeakTypeTag](t: c.Expr[T], columnSeparator: c.Expr[Char]): c.Expr[String] = { def macroImpl[T: c.WeakTypeTag](t: c.Expr[T], columnSeparator: c.Expr[Char]): c.Expr[String] = {
val (names, indexTypes) = super.checkCaseClassZip[T] val (names, indexTypes) = super.checkCaseClassZip[T]
val clazzName = c.weakTypeOf[T].typeSymbol.name val clazzName = c.weakTypeOf[T].typeSymbol.name
val innerVarTermName = TermName("_t") val innerVarTermName = TermName("_t")
val indexByName = (i: Int) => TermName(names(i)) val indexByName = (i: Int) => TermName(names(i))
val fieldsToString = indexTypes.map { idxType => val fieldsToString = indexTypes.map { idxType =>
if (idxType._2 <:< typeOf[Option[_]]) { if (idxType._2 <:< typeOf[Option[_]]) {
val genericType = c.typecheck(q"${idxType._2}", c.TYPEmode).tpe.typeArgs.head val genericType = c.typecheck(q"${idxType._2}", c.TYPEmode).tpe.typeArgs.head
......
...@@ -22,13 +22,13 @@ ...@@ -22,13 +22,13 @@
package org.bitlap.csv.core.macros package org.bitlap.csv.core.macros
import scala.collection.mutable import scala.collection.mutable
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/5/1 * @version 1.0,2022/5/1
*/ */
object MacroCache { object MacroCache {
private var builderCount = 0 private var builderCount = 0
private var identityCount = 0 private var identityCount = 0
def getBuilderId: Int = builderCount.synchronized { def getBuilderId: Int = builderCount.synchronized {
......
...@@ -25,14 +25,14 @@ import org.scalatest.flatspec.AnyFlatSpec ...@@ -25,14 +25,14 @@ import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
import org.bitlap.csv.core.Converter import org.bitlap.csv.core.Converter
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/4/29 * @version 1.0,2022/4/29
*/ */
class CsvConverterTest extends AnyFlatSpec with Matchers { class CsvConverterTest extends AnyFlatSpec with Matchers {
"CsvConverter1" should "ok" in { "CsvConverter1" should "ok" in {
val line = "abc,cdf,d,12,2,false,0.1,0.23333" val line = "abc,cdf,d,12,2,false,0.1,0.23333"
val dimension = Converter[Dimension].toScala(line) val dimension = Converter[Dimension].toScala(line)
assert(dimension.toString == "Some(Dimension(abc,Some(cdf),d,12,2,false,0.1,0.23333))") assert(dimension.toString == "Some(Dimension(abc,Some(cdf),d,12,2,false,0.1,0.23333))")
val csv = Converter[Dimension].toCsvString(dimension.orNull) val csv = Converter[Dimension].toCsvString(dimension.orNull)
...@@ -90,7 +90,7 @@ class CsvConverterTest extends AnyFlatSpec with Matchers { ...@@ -90,7 +90,7 @@ class CsvConverterTest extends AnyFlatSpec with Matchers {
} }
"CsvConverter6" should "ok when using json value" in { "CsvConverter6" should "ok when using json value" in {
val line = """abc,"{""a"":""b"",""c"":""d""}",d,12,2,false,0.1,0.23333""" val line = """abc,"{""a"":""b"",""c"":""d""}",d,12,2,false,0.1,0.23333"""
val dimension = Converter[Dimension].toScala(line) val dimension = Converter[Dimension].toScala(line)
println(dimension) println(dimension)
assert(dimension.toString == "Some(Dimension(abc,Some({\"a\":\"b\",\"c\":\"d\"}),d,12,2,false,0.1,0.23333))") assert(dimension.toString == "Some(Dimension(abc,Some({\"a\":\"b\",\"c\":\"d\"}),d,12,2,false,0.1,0.23333))")
......
...@@ -30,11 +30,11 @@ import org.bitlap.csv.core.CsvableBuilder ...@@ -30,11 +30,11 @@ import org.bitlap.csv.core.CsvableBuilder
import org.bitlap.csv.core.ScalableHelper import org.bitlap.csv.core.ScalableHelper
import java.io.File import java.io.File
/** /** Complex use of common tests
* Complex use of common tests
* *
* @author 梦境迷离 * @author
* @version 1.0,2022/5/1 * 梦境迷离
* @version 1.0,2022/5/1
*/ */
class CsvableAndScalableTest extends AnyFlatSpec with Matchers { class CsvableAndScalableTest extends AnyFlatSpec with Matchers {
...@@ -199,15 +199,14 @@ class CsvableAndScalableTest extends AnyFlatSpec with Matchers { ...@@ -199,15 +199,14 @@ class CsvableAndScalableTest extends AnyFlatSpec with Matchers {
"CsvableAndScalable7" should "ok macro expose code" in { "CsvableAndScalable7" should "ok macro expose code" in {
// to scala // to scala
lazy val _ScalableBuilderFunction$dimensions: String => List[org.bitlap.csv.core.test.Dimension3] = ( lazy val _ScalableBuilderFunction$dimensions: String => List[org.bitlap.csv.core.test.Dimension3] =
(dims: String) => (dims: String) =>
org.bitlap.csv.core.StringUtils.extractJsonValues[org.bitlap.csv.core.test.Dimension3](dims)( org.bitlap.csv.core.StringUtils.extractJsonValues[org.bitlap.csv.core.test.Dimension3](dims)(
((k: String, v: String) => Dimension3.apply(k, v)) (k: String, v: String) => Dimension3.apply(k, v)
) );
);
object _ScalaAnno$1 extends _root_.org.bitlap.csv.core.Scalable[Metric2] { object _ScalaAnno$1 extends _root_.org.bitlap.csv.core.Scalable[Metric2] {
var _line: String = _; var _line: String = _;
private val _columns = (() => _root_.org.bitlap.csv.core.StringUtils.splitColumns(_ScalaAnno$1._line, ',')); private val _columns = () => _root_.org.bitlap.csv.core.StringUtils.splitColumns(_ScalaAnno$1._line, ',');
override def _toScala(column: String): Option[Metric2] = Option( override def _toScala(column: String): Option[Metric2] = Option(
Metric2( Metric2(
...@@ -225,30 +224,26 @@ class CsvableAndScalableTest extends AnyFlatSpec with Matchers { ...@@ -225,30 +224,26 @@ class CsvableAndScalableTest extends AnyFlatSpec with Matchers {
val metrics = scala.Predef val metrics = scala.Predef
.wrapRefArray[String](CsvableAndScalableTest.this.csvData.split("\n")) .wrapRefArray[String](CsvableAndScalableTest.this.csvData.split("\n"))
.toList .toList
.map(((_l: String) => { .map { (_l: String) =>
_scalableInstance._line = _l; _scalableInstance._line = _l;
_scalableInstance._toScala(_l) _scalableInstance._toScala(_l)
})) }
metrics.foreach(println) metrics.foreach(println)
// to csv // to csv
lazy val _CsvableBuilderFunction$dimensions: Seq[org.bitlap.csv.core.test.Dimension3] => String = ( lazy val _CsvableBuilderFunction$dimensions: Seq[org.bitlap.csv.core.test.Dimension3] => String =
(ds: Seq[org.bitlap.csv.core.test.Dimension3]) => (ds: Seq[org.bitlap.csv.core.test.Dimension3]) =>
("\"{" "\"{"
.+( .+(
ds.map[String]( ds.map[String]((kv: org.bitlap.csv.core.test.Dimension3) =>
( "\"\"".+(kv.key).+("\"\":\"\"").+(kv.value).+("\"\""): String
(kv: org.bitlap.csv.core.test.Dimension3) =>
("\"\"".+(kv.key).+("\"\":\"\"").+(kv.value).+("\"\""): String)
)
).mkString(",") ).mkString(",")
) )
.+("}\""): String) .+("}\""): String;
);
object _CsvAnno$2 extends _root_.org.bitlap.csv.core.Csvable[Metric2] { object _CsvAnno$2 extends _root_.org.bitlap.csv.core.Csvable[Metric2] {
var _tt: Metric2 = _; var _tt: Metric2 = _;
lazy private val toCsv = ((temp: Metric2) => { lazy private val toCsv = (temp: Metric2) => {
val fields = Metric2.unapply(temp).orNull; val fields = Metric2.unapply(temp).orNull;
if (null.$eq$eq(fields)) if (null.$eq$eq(fields))
"" ""
...@@ -262,18 +257,18 @@ class CsvableAndScalableTest extends AnyFlatSpec with Matchers { ...@@ -262,18 +257,18 @@ class CsvableAndScalableTest extends AnyFlatSpec with Matchers {
_root_.org.bitlap.csv.core.Csvable[Int]._toCsvString(temp.metricValue) _root_.org.bitlap.csv.core.Csvable[Int]._toCsvString(temp.metricValue)
) )
.mkString(','.toString) .mkString(','.toString)
}); };
override def _toCsvString(t: Metric2): String = toCsv(_CsvAnno$2._tt) override def _toCsvString(t: Metric2): String = toCsv(_CsvAnno$2._tt)
}; };
lazy val _csvableInstance = _CsvAnno$2; lazy val _csvableInstance = _CsvAnno$2;
val csv = metrics val csv = metrics
.filter(((x$3: Option[org.bitlap.csv.core.test.Metric2]) => x$3.isDefined)) .filter((x$3: Option[org.bitlap.csv.core.test.Metric2]) => x$3.isDefined)
.map[org.bitlap.csv.core.test.Metric2](((x$4: Option[org.bitlap.csv.core.test.Metric2]) => x$4.get)) .map[org.bitlap.csv.core.test.Metric2]((x$4: Option[org.bitlap.csv.core.test.Metric2]) => x$4.get)
.map(((_t: Metric2) => { .map { (_t: Metric2) =>
_csvableInstance._tt = _t; _csvableInstance._tt = _t;
_csvableInstance._toCsvString(_t) _csvableInstance._toCsvString(_t)
})) }
.mkString("\n") .mkString("\n")
println(csv) println(csv)
......
...@@ -25,14 +25,14 @@ import org.bitlap.csv.core.{ CsvableBuilder, ScalableBuilder } ...@@ -25,14 +25,14 @@ import org.bitlap.csv.core.{ CsvableBuilder, ScalableBuilder }
import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/4/29 * @version 1.0,2022/4/29
*/ */
class CustomConverterBuilderTest extends AnyFlatSpec with Matchers { class CustomConverterBuilderTest extends AnyFlatSpec with Matchers {
"CustomConverterBuilder1" should "ok" in { "CustomConverterBuilder1" should "ok" in {
val line = "abc,cdf,d,12,2,false,0.1,0.23333" val line = "abc,cdf,d,12,2,false,0.1,0.23333"
val dimension = ScalableBuilder[Dimension2].convert(line, ',') val dimension = ScalableBuilder[Dimension2].convert(line, ',')
assert(dimension.toString == "Some(Dimension2(abc,Some(cdf),d,12,2,false,0.1,0.23333))") assert(dimension.toString == "Some(Dimension2(abc,Some(cdf),d,12,2,false,0.1,0.23333))")
val csv = CsvableBuilder[Dimension2].convert(dimension.get, ',') val csv = CsvableBuilder[Dimension2].convert(dimension.get, ',')
...@@ -103,7 +103,7 @@ class CustomConverterBuilderTest extends AnyFlatSpec with Matchers { ...@@ -103,7 +103,7 @@ class CustomConverterBuilderTest extends AnyFlatSpec with Matchers {
val dimension1 = es.map(e => CsvableBuilder[Dimension2].convert(e, ',')) val dimension1 = es.map(e => CsvableBuilder[Dimension2].convert(e, ','))
assert(dimension1 == List("1,hello,c,1,1,true,0.1,0.2", "2,hello bitlap,c,1,1,false,0.1,0.2")) assert(dimension1 == List("1,hello,c,1,1,true,0.1,0.2", "2,hello bitlap,c,1,1,false,0.1,0.2"))
val csv = List("1,hello,c,1,1,true,0.1,0.2", "2,hello bitlap,c,1,1,false,0.1,0.2") val csv = List("1,hello,c,1,1,true,0.1,0.2", "2,hello bitlap,c,1,1,false,0.1,0.2")
val scala = csv.map(f => ScalableBuilder[Dimension2].convert(f, ',')) val scala = csv.map(f => ScalableBuilder[Dimension2].convert(f, ','))
assert( assert(
scala.toString() == "List(Some(Dimension2(1,Some(hello),c,1,1,true,0.1,0.2)), Some(Dimension2(2,Some(hello bitlap),c,1,1,false,0.1,0.2)))" scala.toString() == "List(Some(Dimension2(1,Some(hello),c,1,1,true,0.1,0.2)), Some(Dimension2(2,Some(hello bitlap),c,1,1,false,0.1,0.2)))"
...@@ -111,8 +111,8 @@ class CustomConverterBuilderTest extends AnyFlatSpec with Matchers { ...@@ -111,8 +111,8 @@ class CustomConverterBuilderTest extends AnyFlatSpec with Matchers {
} }
private val csv = """100,1,"{""city"":""北京"",""os"":""Mac""}",vv,1""" private val csv = """100,1,"{""city"":""北京"",""os"":""Mac""}",vv,1"""
private val metric = Metric(1, 2, Nil, "name", 2) private val metric = Metric(1, 2, Nil, "name", 2)
private val metric2 = Metric2(1, 2, Nil, "name", 2) private val metric2 = Metric2(1, 2, Nil, "name", 2)
"CustomConverterBuilder6" should "fail when find List or Seq but without using setFiled" in { "CustomConverterBuilder6" should "fail when find List or Seq but without using setFiled" in {
...@@ -137,7 +137,7 @@ class CustomConverterBuilderTest extends AnyFlatSpec with Matchers { ...@@ -137,7 +137,7 @@ class CustomConverterBuilderTest extends AnyFlatSpec with Matchers {
} }
"CustomConverterBuilder8" should "ok when not pass columnSeparator" in { "CustomConverterBuilder8" should "ok when not pass columnSeparator" in {
val e = Dimension2("1", Some("hello"), 'c', 1L, 1, false, 0.1f, 0.2) val e = Dimension2("1", Some("hello"), 'c', 1L, 1, false, 0.1f, 0.2)
val csv = CsvableBuilder[Dimension2].convert(e) val csv = CsvableBuilder[Dimension2].convert(e)
println(csv) println(csv)
assert(csv == "1,hello,c,1,1,false,0.1,0.2") assert(csv == "1,hello,c,1,1,false,0.1,0.2")
......
...@@ -24,9 +24,9 @@ package org.bitlap.csv.core.test ...@@ -24,9 +24,9 @@ package org.bitlap.csv.core.test
import org.bitlap.csv.core.Converter import org.bitlap.csv.core.Converter
import org.bitlap.csv.core.macros.{ DeriveToCaseClass, DeriveToString } import org.bitlap.csv.core.macros.{ DeriveToCaseClass, DeriveToString }
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/4/29 * @version 1.0,2022/4/29
*/ */
case class Dimension(key: String, value: Option[String], d: Char, c: Long, e: Short, f: Boolean, g: Float, h: Double) case class Dimension(key: String, value: Option[String], d: Char, c: Long, e: Short, f: Boolean, g: Float, h: Double)
......
...@@ -28,15 +28,15 @@ import org.scalatest.flatspec.AnyFlatSpec ...@@ -28,15 +28,15 @@ import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
import java.io.{ BufferedReader, InputStreamReader } import java.io.{ BufferedReader, InputStreamReader }
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/4/30 * @version 1.0,2022/4/30
*/ */
class StringUtilsTest extends AnyFlatSpec with Matchers { class StringUtilsTest extends AnyFlatSpec with Matchers {
"StringUtilsTest1" should "ok" in { "StringUtilsTest1" should "ok" in {
val line = """abc,"{""a"":""b"",""c"":""d""}",d,12,2,false,0.1,0.23333""" val line = """abc,"{""a"":""b"",""c"":""d""}",d,12,2,false,0.1,0.23333"""
val csv = StringUtils.splitColumns(line, ',') val csv = StringUtils.splitColumns(line, ',')
println(csv) println(csv)
assert(csv.size == 8) assert(csv.size == 8)
} }
...@@ -44,13 +44,13 @@ class StringUtilsTest extends AnyFlatSpec with Matchers { ...@@ -44,13 +44,13 @@ class StringUtilsTest extends AnyFlatSpec with Matchers {
"StringUtilsTest2" should "ok" in { "StringUtilsTest2" should "ok" in {
// only extract json `"{""a"":""b"",""c"":""d""}"` // only extract json `"{""a"":""b"",""c"":""d""}"`
val line = """abc,"{""a"":""b"",""c"":""d""}",d,12,2,false,0.1,0.23333""" val line = """abc,"{""a"":""b"",""c"":""d""}",d,12,2,false,0.1,0.23333"""
val csv = StringUtils.extractJsonValues[Dimension3](line)((k, v) => Dimension3(k, v)) val csv = StringUtils.extractJsonValues[Dimension3](line)((k, v) => Dimension3(k, v))
println(csv) println(csv)
assert(csv.toString() == "List(Dimension3(\"a\",\"b\"), Dimension3(\"c\",\"d\"))") assert(csv.toString() == "List(Dimension3(\"a\",\"b\"), Dimension3(\"c\",\"d\"))")
} }
"StringUtilsTest3" should "ok for file" in { "StringUtilsTest3" should "ok for file" in {
val reader = new InputStreamReader(ClassLoader.getSystemResourceAsStream("simple_data.csv")) val reader = new InputStreamReader(ClassLoader.getSystemResourceAsStream("simple_data.csv"))
val bufferedReader = new BufferedReader(reader) val bufferedReader = new BufferedReader(reader)
FileUtils.using(bufferedReader) { input => FileUtils.using(bufferedReader) { input =>
var line: String = null var line: String = null
......
...@@ -26,11 +26,11 @@ import org.bitlap.csv.core.macros.AbstractMacroProcessor ...@@ -26,11 +26,11 @@ import org.bitlap.csv.core.macros.AbstractMacroProcessor
import scala.reflect.macros.blackbox import scala.reflect.macros.blackbox
/** /** This is a tool macro for automatic derivation of the base CSV converter.
* This is a tool macro for automatic derivation of the base CSV converter.
* *
* @author 梦境迷离 * @author
* @version 1.0,2022/4/29 * 梦境迷离
* @version 1.0,2022/4/29
*/ */
object DeriveCsvConverter { object DeriveCsvConverter {
...@@ -43,7 +43,7 @@ object DeriveCsvConverter { ...@@ -43,7 +43,7 @@ object DeriveCsvConverter {
def macroImplWithColumnSeparator[CC: c.WeakTypeTag](columnSeparator: c.Expr[Char]): c.Expr[CC] = { def macroImplWithColumnSeparator[CC: c.WeakTypeTag](columnSeparator: c.Expr[Char]): c.Expr[CC] = {
import c.universe._ import c.universe._
val clazzName = c.weakTypeOf[CC].typeSymbol.name val clazzName = c.weakTypeOf[CC].typeSymbol.name
val typeName = TypeName(clazzName.decodedName.toString) val typeName = TypeName(clazzName.decodedName.toString)
val tree = val tree =
q""" q"""
new Converter[$typeName] { new Converter[$typeName] {
......
...@@ -24,9 +24,9 @@ package org.bitlap.csv.derive.test ...@@ -24,9 +24,9 @@ package org.bitlap.csv.derive.test
import org.bitlap.csv.core.Converter import org.bitlap.csv.core.Converter
import org.bitlap.csv.derive.DeriveCsvConverter import org.bitlap.csv.derive.DeriveCsvConverter
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/4/29 * @version 1.0,2022/4/29
*/ */
case class CsvLine(key: String, value: Option[String], d: Char, c: Long, e: Short, f: Boolean, g: Float, h: Double) case class CsvLine(key: String, value: Option[String], d: Char, c: Long, e: Short, f: Boolean, g: Float, h: Double)
......
...@@ -24,9 +24,9 @@ package org.bitlap.csv.derive.test ...@@ -24,9 +24,9 @@ package org.bitlap.csv.derive.test
import org.bitlap.csv.core.Converter import org.bitlap.csv.core.Converter
import org.bitlap.csv.derive.DeriveCsvConverter import org.bitlap.csv.derive.DeriveCsvConverter
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/4/29 * @version 1.0,2022/4/29
*/ */
case class CsvLine2(key: String, value: Option[String]) case class CsvLine2(key: String, value: Option[String])
......
...@@ -27,9 +27,9 @@ import org.bitlap.csv.derive.DeriveCsvConverter ...@@ -27,9 +27,9 @@ import org.bitlap.csv.derive.DeriveCsvConverter
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/4/29 * @version 1.0,2022/4/29
*/ */
case class CsvLine3(key: String, time: LocalDateTime) case class CsvLine3(key: String, time: LocalDateTime)
......
...@@ -24,9 +24,9 @@ package org.bitlap.csv.derive.test ...@@ -24,9 +24,9 @@ package org.bitlap.csv.derive.test
import org.bitlap.csv.core.{ Converter, StringUtils } import org.bitlap.csv.core.{ Converter, StringUtils }
import org.bitlap.csv.derive.DeriveCsvConverter import org.bitlap.csv.derive.DeriveCsvConverter
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/5/15 * @version 1.0,2022/5/15
*/ */
case class CsvLine4(time: Long, entity: Int, dimensions: List[Dimension], metricName: String, metricValue: Int) case class CsvLine4(time: Long, entity: Int, dimensions: List[Dimension], metricName: String, metricValue: Int)
......
...@@ -25,9 +25,9 @@ import org.bitlap.csv.core.Converter ...@@ -25,9 +25,9 @@ import org.bitlap.csv.core.Converter
import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/4/29 * @version 1.0,2022/4/29
*/ */
class DeriveCsvConverterTest extends AnyFlatSpec with Matchers { class DeriveCsvConverterTest extends AnyFlatSpec with Matchers {
...@@ -50,7 +50,7 @@ class DeriveCsvConverterTest extends AnyFlatSpec with Matchers { ...@@ -50,7 +50,7 @@ class DeriveCsvConverterTest extends AnyFlatSpec with Matchers {
|200,3,"{""city"":""北京"",""os"":""Mac""}",pv,2""".stripMargin |200,3,"{""city"":""北京"",""os"":""Mac""}",pv,2""".stripMargin
"DeriveCsvConverter1" should "ok" in { "DeriveCsvConverter1" should "ok" in {
val line = "abc,cdf,d,12,2,false,0.1,0.23333" val line = "abc,cdf,d,12,2,false,0.1,0.23333"
val dimension = Converter[CsvLine].toScala(line) val dimension = Converter[CsvLine].toScala(line)
assert(dimension.toString == "Some(CsvLine(abc,Some(cdf),d,12,2,false,0.1,0.23333))") assert(dimension.toString == "Some(CsvLine(abc,Some(cdf),d,12,2,false,0.1,0.23333))")
val csv = Converter[CsvLine].toCsvString(dimension.orNull) val csv = Converter[CsvLine].toCsvString(dimension.orNull)
......
...@@ -21,12 +21,12 @@ ...@@ -21,12 +21,12 @@
package org.bitlap.tools package org.bitlap.tools
/** /** Log Level for elapsed
* Log Level for elapsed
* *
* @author 梦境迷离 * @author
* @since 2021/8/7 * 梦境迷离
* @version 1.0 * @since 2021/8/7
* @version 1.0
*/ */
object LogLevel extends Enumeration { object LogLevel extends Enumeration {
...@@ -35,7 +35,7 @@ object LogLevel extends Enumeration { ...@@ -35,7 +35,7 @@ object LogLevel extends Enumeration {
private[bitlap] def getLogLevel(shortType: String): LogLevel = { private[bitlap] def getLogLevel(shortType: String): LogLevel = {
// TODO not good way // TODO not good way
val tpe1 = s"$PACKAGE.elapsed.$shortType" //LogLevel.INFO val tpe1 = s"$PACKAGE.elapsed.$shortType" // LogLevel.INFO
val tpe2 = s"$PACKAGE.elapsed.LogLevel.$shortType" // INFO val tpe2 = s"$PACKAGE.elapsed.LogLevel.$shortType" // INFO
val v = LogLevel.values.find { p => val v = LogLevel.values.find { p =>
s"$PACKAGE.elapsed.LogLevel.${p.toString}" == tpe1 || s"$PACKAGE.elapsed.LogLevel.${p.toString}" == tpe1 ||
......
...@@ -25,12 +25,12 @@ import org.bitlap.tools.macros.applyMacro ...@@ -25,12 +25,12 @@ import org.bitlap.tools.macros.applyMacro
import scala.annotation.{ compileTimeOnly, StaticAnnotation } import scala.annotation.{ compileTimeOnly, StaticAnnotation }
/** /** annotation to generate apply method for primary construction of ordinary classes.
* annotation to generate apply method for primary construction of ordinary classes.
* *
* @author 梦境迷离 * @author
* @since 2021/6/30 * 梦境迷离
* @version 1.0 * @since 2021/6/30
* @version 1.0
*/ */
@compileTimeOnly("enable macro to expand macro annotations") @compileTimeOnly("enable macro to expand macro annotations")
final class apply extends StaticAnnotation { final class apply extends StaticAnnotation {
......
...@@ -25,12 +25,12 @@ import org.bitlap.tools.macros.builderMacro ...@@ -25,12 +25,12 @@ import org.bitlap.tools.macros.builderMacro
import scala.annotation.{ compileTimeOnly, StaticAnnotation } import scala.annotation.{ compileTimeOnly, StaticAnnotation }
/** /** annotation to generate builder pattern for classes.
* annotation to generate builder pattern for classes.
* *
* @author 梦境迷离 * @author
* @since 2021/6/19 * 梦境迷离
* @version 1.0 * @since 2021/6/19
* @version 1.0
*/ */
@compileTimeOnly("enable macro to expand macro annotations") @compileTimeOnly("enable macro to expand macro annotations")
final class builder extends StaticAnnotation { final class builder extends StaticAnnotation {
......
...@@ -25,13 +25,14 @@ import org.bitlap.tools.macros.constructorMacro ...@@ -25,13 +25,14 @@ import org.bitlap.tools.macros.constructorMacro
import scala.annotation.{ compileTimeOnly, StaticAnnotation } import scala.annotation.{ compileTimeOnly, StaticAnnotation }
/** /** annotation to generate secondary constructor method for classes.
* annotation to generate secondary constructor method for classes.
* *
* @author 梦境迷离 * @author
* @param excludeFields Whether to exclude the specified var fields. * 梦境迷离
* @since 2021/7/3 * @param excludeFields
* @version 1.0 * Whether to exclude the specified var fields.
* @since 2021/7/3
* @version 1.0
*/ */
@compileTimeOnly("enable macro to expand macro annotations") @compileTimeOnly("enable macro to expand macro annotations")
final class constructor(excludeFields: Seq[String] = Nil) extends StaticAnnotation { final class constructor(excludeFields: Seq[String] = Nil) extends StaticAnnotation {
......
...@@ -27,14 +27,16 @@ import org.bitlap.tools.macros.elapsedMacro.ElapsedProcessor ...@@ -27,14 +27,16 @@ import org.bitlap.tools.macros.elapsedMacro.ElapsedProcessor
import scala.annotation.{ compileTimeOnly, StaticAnnotation } import scala.annotation.{ compileTimeOnly, StaticAnnotation }
import scala.concurrent.duration.Duration import scala.concurrent.duration.Duration
/** /** annotation to record method cost time.
* annotation to record method cost time.
* *
* @author 梦境迷离 * @author
* @param limit Time consuming condition to trigger log. * 梦境迷离
* @param logLevel Log Level. * @param limit
* @since 2021/8/7 * Time consuming condition to trigger log.
* @version 1.0 * @param logLevel
* Log Level.
* @since 2021/8/7
* @version 1.0
*/ */
@compileTimeOnly("enable macro to expand macro annotations") @compileTimeOnly("enable macro to expand macro annotations")
final class elapsed(limit: Duration, logLevel: LogLevel) extends StaticAnnotation { final class elapsed(limit: Duration, logLevel: LogLevel) extends StaticAnnotation {
......
...@@ -25,13 +25,14 @@ import org.bitlap.tools.macros.equalsAndHashCodeMacro.EqualsAndHashCodeProcessor ...@@ -25,13 +25,14 @@ import org.bitlap.tools.macros.equalsAndHashCodeMacro.EqualsAndHashCodeProcessor
import scala.annotation.{ compileTimeOnly, StaticAnnotation } import scala.annotation.{ compileTimeOnly, StaticAnnotation }
/** /** annotation to generate equals and hashcode method for classes.
* annotation to generate equals and hashcode method for classes.
* *
* @author 梦境迷离 * @author
* @param excludeFields Whether to exclude the specified internal fields. * 梦境迷离
* @since 2021/7/18 * @param excludeFields
* @version 1.0 * Whether to exclude the specified internal fields.
* @since 2021/7/18
* @version 1.0
*/ */
@compileTimeOnly("enable macro to expand macro annotations") @compileTimeOnly("enable macro to expand macro annotations")
final class equalsAndHashCode(excludeFields: Seq[String] = Nil) extends StaticAnnotation { final class equalsAndHashCode(excludeFields: Seq[String] = Nil) extends StaticAnnotation {
......
...@@ -25,14 +25,16 @@ import org.bitlap.tools.macros.jacksonEnumMacro ...@@ -25,14 +25,16 @@ import org.bitlap.tools.macros.jacksonEnumMacro
import scala.annotation.{ compileTimeOnly, StaticAnnotation } import scala.annotation.{ compileTimeOnly, StaticAnnotation }
/** /** annotation to generate equals and hashcode method for classes.
* annotation to generate equals and hashcode method for classes.
* *
* @author 梦境迷离 * @author
* @author choly * 梦境迷离
* @param nonTypeRefers Whether to not generate the subclass of the TypeReference for paramTypes of class. * @author
* @since 2021/8/3 * choly
* @version 1.0 * @param nonTypeRefers
* Whether to not generate the subclass of the TypeReference for paramTypes of class.
* @since 2021/8/3
* @version 1.0
*/ */
@compileTimeOnly("enable macro to expand macro annotations") @compileTimeOnly("enable macro to expand macro annotations")
final class jacksonEnum(nonTypeRefers: Seq[String] = Nil) extends StaticAnnotation { final class jacksonEnum(nonTypeRefers: Seq[String] = Nil) extends StaticAnnotation {
......
...@@ -24,13 +24,13 @@ package org.bitlap.tools ...@@ -24,13 +24,13 @@ package org.bitlap.tools
import scala.annotation.{ compileTimeOnly, StaticAnnotation } import scala.annotation.{ compileTimeOnly, StaticAnnotation }
import org.bitlap.tools.macros.javaCompatibleMacro import org.bitlap.tools.macros.javaCompatibleMacro
/** /** annotation to generate non-parameter constructor and get/set method for case classes. Fields marked `private[this]`
* annotation to generate non-parameter constructor and get/set method for case classes. * in curry are not supported !
* Fields marked `private[this]` in curry are not supported !
* *
* @author 梦境迷离 * @author
* @since 2021/11/23 * 梦境迷离
* @version 1.0 * @since 2021/11/23
* @version 1.0
*/ */
@compileTimeOnly("enable macro to expand macro annotations") @compileTimeOnly("enable macro to expand macro annotations")
final class javaCompatible extends StaticAnnotation { final class javaCompatible extends StaticAnnotation {
......
...@@ -25,12 +25,12 @@ import org.bitlap.tools.macros.jsonMacro ...@@ -25,12 +25,12 @@ import org.bitlap.tools.macros.jsonMacro
import scala.annotation.{ compileTimeOnly, StaticAnnotation } import scala.annotation.{ compileTimeOnly, StaticAnnotation }
/** /** annotation to generate play-json implicit object for case classes.
* annotation to generate play-json implicit object for case classes.
* *
* @author 梦境迷离 * @author
* @since 2021/6/13 * 梦境迷离
* @version 1.0 * @since 2021/6/13
* @version 1.0
*/ */
@compileTimeOnly("enable macro to expand macro annotations") @compileTimeOnly("enable macro to expand macro annotations")
final class json extends StaticAnnotation { final class json extends StaticAnnotation {
......
...@@ -26,13 +26,14 @@ import org.bitlap.tools.macros.logMacro ...@@ -26,13 +26,14 @@ import org.bitlap.tools.macros.logMacro
import scala.annotation.{ compileTimeOnly, StaticAnnotation } import scala.annotation.{ compileTimeOnly, StaticAnnotation }
/** /** annotation to generate log.
* annotation to generate log.
* *
* @author 梦境迷离 * @author
* @param logType Specifies the type of `log` that needs to be generated * 梦境迷离
* @since 2021/6/28 * @param logType
* @version 1.0 * Specifies the type of `log` that needs to be generated
* @since 2021/6/28
* @version 1.0
*/ */
@compileTimeOnly("enable macro to expand macro annotations") @compileTimeOnly("enable macro to expand macro annotations")
final class log(logType: LogType.LogType = LogType.JLog) extends StaticAnnotation { final class log(logType: LogType.LogType = LogType.JLog) extends StaticAnnotation {
......
...@@ -25,9 +25,9 @@ import org.bitlap.tools.PACKAGE ...@@ -25,9 +25,9 @@ import org.bitlap.tools.PACKAGE
import org.bitlap.tools.logs.extension.{ ScalaLoggingLazyImpl, ScalaLoggingStrictImpl } import org.bitlap.tools.logs.extension.{ ScalaLoggingLazyImpl, ScalaLoggingStrictImpl }
import org.bitlap.tools.logs.impl.{ JLogImpl, Log4J2Impl, Slf4jImpl } import org.bitlap.tools.logs.impl.{ JLogImpl, Log4J2Impl, Slf4jImpl }
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/3/29 * @version 1.0,2022/3/29
*/ */
object LogType extends Enumeration { object LogType extends Enumeration {
...@@ -35,11 +35,11 @@ object LogType extends Enumeration { ...@@ -35,11 +35,11 @@ object LogType extends Enumeration {
val JLog, Log4j2, Slf4j, ScalaLoggingLazy, ScalaLoggingStrict = Value val JLog, Log4j2, Slf4j, ScalaLoggingLazy, ScalaLoggingStrict = Value
private lazy val types: Map[LogType, BaseLog] = Map( private lazy val types: Map[LogType, BaseLog] = Map(
JLogImpl.`type` -> JLogImpl, JLogImpl.`type` -> JLogImpl,
Log4J2Impl.`type` -> Log4J2Impl, Log4J2Impl.`type` -> Log4J2Impl,
Slf4jImpl.`type` -> Slf4jImpl, Slf4jImpl.`type` -> Slf4jImpl,
ScalaLoggingStrictImpl.`type` -> ScalaLoggingStrictImpl, ScalaLoggingStrictImpl.`type` -> ScalaLoggingStrictImpl,
ScalaLoggingLazyImpl.`type` -> ScalaLoggingLazyImpl ScalaLoggingLazyImpl.`type` -> ScalaLoggingLazyImpl
) )
def getLogImpl(logType: LogType): BaseLog = def getLogImpl(logType: LogType): BaseLog =
...@@ -47,12 +47,14 @@ object LogType extends Enumeration { ...@@ -47,12 +47,14 @@ object LogType extends Enumeration {
// TODO not use Enumeratio // TODO not use Enumeratio
def getLogType(shortType: String): LogType = { def getLogType(shortType: String): LogType = {
val tpe1 = s"$PACKAGE.logs.$shortType" //LogType.JLog val tpe1 = s"$PACKAGE.logs.$shortType" // LogType.JLog
val tpe2 = s"$PACKAGE.logs.LogType.$shortType" // JLog val tpe2 = s"$PACKAGE.logs.LogType.$shortType" // JLog
val v = LogType.values.find { p => val v = LogType.values.find { p =>
s"$PACKAGE.logs.LogType.${p.toString}" == tpe1 || s"$PACKAGE.logs.LogType.${p.toString}" == tpe1 ||
s"$PACKAGE.logs.LogType.${p.toString}" == tpe2 || s"$PACKAGE.logs.LogType.${p.toString}" == shortType s"$PACKAGE.logs.LogType.${p.toString}" == tpe2 || s"$PACKAGE.logs.LogType.${p.toString}" == shortType
}.getOrElse(throw new Exception(s"Not support log type: $shortType")).toString }
.getOrElse(throw new Exception(s"Not support log type: $shortType"))
.toString
LogType.withName(v) LogType.withName(v)
} }
} }
...@@ -23,9 +23,8 @@ package org.bitlap.tools.logs.extension ...@@ -23,9 +23,8 @@ package org.bitlap.tools.logs.extension
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
/** /** Defines `logger` as a lazy value initialized with an underlying `org.slf4j.Logger` named according to the class into
* Defines `logger` as a lazy value initialized with an underlying `org.slf4j.Logger` * which this trait is mixed.
* named according to the class into which this trait is mixed.
*/ */
trait ScalaLazyLogging { trait ScalaLazyLogging {
......
...@@ -26,12 +26,12 @@ import org.bitlap.tools.logs.{ BaseLog, LogArgument, LogType } ...@@ -26,12 +26,12 @@ import org.bitlap.tools.logs.{ BaseLog, LogArgument, LogType }
import scala.reflect.macros.whitebox import scala.reflect.macros.whitebox
/** /** scalalogging LazyLogging
* scalalogging LazyLogging
* *
* @author 梦境迷离 * @author
* @since 2021/7/26 * 梦境迷离
* @version 1.0 * @since 2021/7/26
* @version 1.0
*/ */
object ScalaLoggingLazyImpl extends BaseLog { object ScalaLoggingLazyImpl extends BaseLog {
......
...@@ -26,12 +26,12 @@ import org.bitlap.tools.logs.{ BaseLog, LogArgument, LogType } ...@@ -26,12 +26,12 @@ import org.bitlap.tools.logs.{ BaseLog, LogArgument, LogType }
import scala.reflect.macros.whitebox import scala.reflect.macros.whitebox
/** /** * scalalogging StrictLogging
* * scalalogging StrictLogging
* *
* @author 梦境迷离 * @author
* @since 2021/7/26 * 梦境迷离
* @version 1.0 * @since 2021/7/26
* @version 1.0
*/ */
object ScalaLoggingStrictImpl extends BaseLog { object ScalaLoggingStrictImpl extends BaseLog {
......
...@@ -23,9 +23,8 @@ package org.bitlap.tools.logs.extension ...@@ -23,9 +23,8 @@ package org.bitlap.tools.logs.extension
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
/** /** Defines `logger` as a value initialized with an underlying `org.slf4j.Logger` named according to the class into
* Defines `logger` as a value initialized with an underlying `org.slf4j.Logger` * which this trait is mixed.
* named according to the class into which this trait is mixed.
*/ */
trait ScalaStrictLogging { trait ScalaStrictLogging {
......
...@@ -26,9 +26,9 @@ import org.bitlap.tools.logs.LogType.LogType ...@@ -26,9 +26,9 @@ import org.bitlap.tools.logs.LogType.LogType
import scala.reflect.macros.whitebox import scala.reflect.macros.whitebox
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/3/29 * @version 1.0,2022/3/29
*/ */
object JLogImpl extends BaseLog { object JLogImpl extends BaseLog {
...@@ -38,12 +38,12 @@ object JLogImpl extends BaseLog { ...@@ -38,12 +38,12 @@ object JLogImpl extends BaseLog {
import c.universe._ import c.universe._
if (logArgument.isClass) { if (logArgument.isClass) {
q"""@transient private final val log: java.util.logging.Logger = java.util.logging.Logger.getLogger(classOf[${TypeName( q"""@transient private final val log: java.util.logging.Logger = java.util.logging.Logger.getLogger(classOf[${TypeName(
logArgument.classNameStr logArgument.classNameStr
)}].getName)""" )}].getName)"""
} else { } else {
q"""@transient private final val log: java.util.logging.Logger = java.util.logging.Logger.getLogger(${TermName( q"""@transient private final val log: java.util.logging.Logger = java.util.logging.Logger.getLogger(${TermName(
logArgument.classNameStr logArgument.classNameStr
)}.getClass.getName)""" )}.getClass.getName)"""
} }
} }
......
...@@ -26,9 +26,9 @@ import org.bitlap.tools.logs.LogType.LogType ...@@ -26,9 +26,9 @@ import org.bitlap.tools.logs.LogType.LogType
import scala.reflect.macros.whitebox import scala.reflect.macros.whitebox
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/3/29 * @version 1.0,2022/3/29
*/ */
object Log4J2Impl extends BaseLog { object Log4J2Impl extends BaseLog {
...@@ -38,12 +38,12 @@ object Log4J2Impl extends BaseLog { ...@@ -38,12 +38,12 @@ object Log4J2Impl extends BaseLog {
import c.universe._ import c.universe._
if (logArgument.isClass) { if (logArgument.isClass) {
q"""@transient private final val log: org.apache.logging.log4j.Logger = org.apache.logging.log4j.LogManager.getLogger(classOf[${TypeName( q"""@transient private final val log: org.apache.logging.log4j.Logger = org.apache.logging.log4j.LogManager.getLogger(classOf[${TypeName(
logArgument.classNameStr logArgument.classNameStr
)}].getName)""" )}].getName)"""
} else { } else {
q"""@transient private final val log: org.apache.logging.log4j.Logger = org.apache.logging.log4j.LogManager.getLogger(${TermName( q"""@transient private final val log: org.apache.logging.log4j.Logger = org.apache.logging.log4j.LogManager.getLogger(${TermName(
logArgument.classNameStr logArgument.classNameStr
)}.getClass.getName)""" )}.getClass.getName)"""
} }
} }
......
...@@ -26,9 +26,9 @@ import org.bitlap.tools.logs.LogType.LogType ...@@ -26,9 +26,9 @@ import org.bitlap.tools.logs.LogType.LogType
import scala.reflect.macros.whitebox import scala.reflect.macros.whitebox
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/3/29 * @version 1.0,2022/3/29
*/ */
object Slf4jImpl extends BaseLog { object Slf4jImpl extends BaseLog {
...@@ -38,12 +38,12 @@ object Slf4jImpl extends BaseLog { ...@@ -38,12 +38,12 @@ object Slf4jImpl extends BaseLog {
import c.universe._ import c.universe._
if (logArgument.isClass) { if (logArgument.isClass) {
q"""@transient private final val log: org.slf4j.Logger = org.slf4j.LoggerFactory.getLogger(classOf[${TypeName( q"""@transient private final val log: org.slf4j.Logger = org.slf4j.LoggerFactory.getLogger(classOf[${TypeName(
logArgument.classNameStr logArgument.classNameStr
)}])""" )}])"""
} else { } else {
q"""@transient private final val log: org.slf4j.Logger = org.slf4j.LoggerFactory.getLogger(${TermName( q"""@transient private final val log: org.slf4j.Logger = org.slf4j.LoggerFactory.getLogger(${TermName(
logArgument.classNameStr logArgument.classNameStr
)}.getClass)""" )}.getClass)"""
} }
} }
} }
...@@ -25,15 +25,15 @@ import org.bitlap.tools.logs.LogType.LogType ...@@ -25,15 +25,15 @@ import org.bitlap.tools.logs.LogType.LogType
import scala.reflect.macros.whitebox import scala.reflect.macros.whitebox
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/3/29 * @version 1.0,2022/3/29
*/ */
package object logs { package object logs {
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2022/3/29 * @version 1.0,2022/3/29
*/ */
private[tools] trait BaseLog { private[tools] trait BaseLog {
...@@ -42,10 +42,12 @@ package object logs { ...@@ -42,10 +42,12 @@ package object logs {
def getTemplate(c: whitebox.Context)(logArgument: LogArgument): c.Tree def getTemplate(c: whitebox.Context)(logArgument: LogArgument): c.Tree
} }
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @param classNameStr The class Name. * @param classNameStr
* @param isClass Is it a class? * The class Name.
* @param isClass
* Is it a class?
*/ */
private[tools] case class LogArgument(classNameStr: String, isClass: Boolean) private[tools] case class LogArgument(classNameStr: String, isClass: Boolean)
......
...@@ -26,10 +26,10 @@ import java.time.format.DateTimeFormatter ...@@ -26,10 +26,10 @@ import java.time.format.DateTimeFormatter
import scala.annotation.tailrec import scala.annotation.tailrec
import scala.reflect.macros.whitebox import scala.reflect.macros.whitebox
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2021/7/24 * @since 2021/7/24
* @version 1.0 * @version 1.0
*/ */
abstract class AbstractMacroProcessor(val c: whitebox.Context) { abstract class AbstractMacroProcessor(val c: whitebox.Context) {
...@@ -37,20 +37,21 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) { ...@@ -37,20 +37,21 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) {
protected final lazy val SDKClasses = Set("java.lang.Object", "scala.AnyRef") protected final lazy val SDKClasses = Set("java.lang.Object", "scala.AnyRef")
/** /** Subclasses should override the method and return the final result abstract syntax tree, or an abstract syntax tree
* Subclasses should override the method and return the final result abstract syntax tree, or an abstract syntax tree close to the final result. * close to the final result.
* *
* @param classDecl * @param classDecl
* @param compDeclOpt * @param compDeclOpt
* @return `c.Expr[Any]`, Why use Any? The dependent type need aux-pattern in scala2. Now let's get around this. * @return
* `c.Expr[Any]`, Why use Any? The dependent type need aux-pattern in scala2. Now let's get around this.
*/ */
def createCustomExpr(classDecl: ClassDef, compDeclOpt: Option[ModuleDef] = None): Any = ??? def createCustomExpr(classDecl: ClassDef, compDeclOpt: Option[ModuleDef] = None): Any = ???
/** /** Subclasses should override this method if it cannot use [[createCustomExpr]] method.
* Subclasses should override this method if it cannot use [[createCustomExpr]] method.
* *
* @param annottees * @param annottees
* @return Return a macro expanded final syntax tree. * @return
* Return a macro expanded final syntax tree.
*/ */
def impl(annottees: Expr[Any]*): Expr[Any] = { def impl(annottees: Expr[Any]*): Expr[Any] = {
checkAnnottees(annottees) checkAnnottees(annottees)
...@@ -59,27 +60,24 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) { ...@@ -59,27 +60,24 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) {
resTree resTree
} }
/** /** Check the input tree of the annotation.
* Check the input tree of the annotation.
* *
* @param annottees * @param annottees
*/ */
def checkAnnottees(annottees: Seq[Expr[Any]]): Unit = {} def checkAnnottees(annottees: Seq[Expr[Any]]): Unit = {}
/** /** Eval the input tree.
* Eval the input tree.
* *
* @param tree * @param tree
* @tparam T * @tparam T
* @return * @return
*/ */
def evalTree[T: WeakTypeTag](tree: Tree): T = c.eval(c.Expr[T](c.untypecheck(tree.duplicate))) def evalTree[T: WeakTypeTag](tree: Tree): T = c.eval(c.Expr[T](c.untypecheck(tree.duplicate)))
/** /** Output ast result.
* Output ast result.
* *
* @param force * @param force
* @param resTree * @param resTree
*/ */
def printTree(force: Boolean, resTree: Tree): Unit = def printTree(force: Boolean, resTree: Tree): Unit =
c.info( c.info(
...@@ -89,24 +87,24 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) { ...@@ -89,24 +87,24 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) {
force = false force = false
) )
/** /** Check the class and its companion object, and return the class definition.
* Check the class and its companion object, and return the class definition.
* *
* @param annottees * @param annottees
* @return Return a [[scala.reflect.api.Trees#ClassDef]] * @return
* Return a [[scala.reflect.api.Trees#ClassDef]]
*/ */
def checkGetClassDef(annottees: Seq[Expr[Any]]): ClassDef = def checkGetClassDef(annottees: Seq[Expr[Any]]): ClassDef =
annottees.map(_.tree).toList match { annottees.map(_.tree).toList match {
case (classDecl: ClassDef) :: Nil => classDecl case (classDecl: ClassDef) :: Nil => classDecl
case (classDecl: ClassDef) :: (_: ModuleDef) :: Nil => classDecl case (classDecl: ClassDef) :: (_: ModuleDef) :: Nil => classDecl
case _ => c.abort(c.enclosingPosition, ErrorMessage.UNEXPECTED_PATTERN) case _ => c.abort(c.enclosingPosition, ErrorMessage.UNEXPECTED_PATTERN)
} }
/** /** Get object if it exists.
* Get object if it exists.
* *
* @param annottees * @param annottees
* @return Return a optional [[scala.reflect.api.Trees#ModuleDef]] * @return
* Return a optional [[scala.reflect.api.Trees#ModuleDef]]
*/ */
def getModuleDefOption(annottees: Seq[Expr[Any]]): Option[ModuleDef] = def getModuleDefOption(annottees: Seq[Expr[Any]]): Option[ModuleDef] =
annottees.map(_.tree).toList match { annottees.map(_.tree).toList match {
...@@ -117,12 +115,13 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) { ...@@ -117,12 +115,13 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) {
case _ => None case _ => None
} }
/** /** Modify the associated object itself according to whether there is an associated object.
* Modify the associated object itself according to whether there is an associated object.
* *
* @param annottees * @param annottees
* @param modifyAction The actual processing function * @param modifyAction
* @return Return the result of modifyAction function * The actual processing function
* @return
* Return the result of modifyAction function
*/ */
def collectCustomExpr( def collectCustomExpr(
annottees: Seq[Expr[Any]] annottees: Seq[Expr[Any]]
...@@ -132,27 +131,29 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) { ...@@ -132,27 +131,29 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) {
modifyAction(classDef, compDecl).asInstanceOf[Expr[Nothing]] modifyAction(classDef, compDecl).asInstanceOf[Expr[Nothing]]
} }
/** /** Check whether the class is a case class.
* Check whether the class is a case class.
* *
* @param annotateeClass classDef * @param annotateeClass
* @return Return true if it is a case class * classDef
* @return
* Return true if it is a case class
*/ */
def isCaseClass(annotateeClass: ClassDef): Boolean = def isCaseClass(annotateeClass: ClassDef): Boolean =
annotateeClass.mods.hasFlag(Flag.CASE) annotateeClass.mods.hasFlag(Flag.CASE)
/** /** Check whether the mods of the fields has a `private[this]` or `protected[this]`, because it cannot be used out of
* Check whether the mods of the fields has a `private[this]` or `protected[this]`, because it cannot be used out of class. * class.
* *
* @param tree Tree is a field or method? * @param tree
* @return Return false if mods exists `private[this]` or `protected[this]` * Tree is a field or method?
* @return
* Return false if mods exists `private[this]` or `protected[this]`
*/ */
def isNotLocalClassMember(tree: Tree): Boolean = { def isNotLocalClassMember(tree: Tree): Boolean = {
lazy val modifierNotLocal = (mods: Modifiers) => { lazy val modifierNotLocal = (mods: Modifiers) =>
!( !(
mods.hasFlag(Flag.PRIVATE | Flag.LOCAL) | mods.hasFlag(Flag.PROTECTED | Flag.LOCAL) mods.hasFlag(Flag.PRIVATE | Flag.LOCAL) | mods.hasFlag(Flag.PROTECTED | Flag.LOCAL)
) )
}
tree match { tree match {
case v: ValDef => modifierNotLocal(v.mods) case v: ValDef => modifierNotLocal(v.mods)
case d: DefDef => modifierNotLocal(d.mods) case d: DefDef => modifierNotLocal(d.mods)
...@@ -160,36 +161,37 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) { ...@@ -160,36 +161,37 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) {
} }
} }
/** /** Get the field TermName with type.
* Get the field TermName with type.
* *
* @param annotteeClassParams * @param annotteeClassParams
* @return Return a sequence of [[scala.reflect.api.Trees#Tree]], each one is `tname: tpt` * @return
* Return a sequence of [[scala.reflect.api.Trees#Tree]], each one is `tname: tpt`
*/ */
def getConstructorParamsNameWithType(annotteeClassParams: Seq[Tree]): Seq[Tree] = def getConstructorParamsNameWithType(annotteeClassParams: Seq[Tree]): Seq[Tree] =
annotteeClassParams.map(_.asInstanceOf[ValDef]).map(v => q"${v.name}: ${v.tpt}") annotteeClassParams.map(_.asInstanceOf[ValDef]).map(v => q"${v.name}: ${v.tpt}")
/** /** Modify companion object or object.
* Modify companion object or object.
* *
* @param compDeclOpt * @param compDeclOpt
* @param codeBlocks * @param codeBlocks
* @param className * @param className
* @return Return a [[scala.reflect.api.Trees#ModuleDef]] which was appended codeblocks, ModuleDef may already exist or may be newly created * @return
* Return a [[scala.reflect.api.Trees#ModuleDef]] which was appended codeblocks, ModuleDef may already exist or may
* be newly created
*/ */
def appendModuleBody(compDeclOpt: Option[ModuleDef], codeBlocks: List[Tree], className: TypeName): Tree = def appendModuleBody(compDeclOpt: Option[ModuleDef], codeBlocks: List[Tree], className: TypeName): Tree =
compDeclOpt.fold(q"object ${className.toTermName} { ..$codeBlocks }") { compDecl => compDeclOpt.fold(q"object ${className.toTermName} { ..$codeBlocks }") { compDecl =>
val ModuleDef(mods, name, impl) = compDecl val ModuleDef(mods, name, impl) = compDecl
val Template(parents, self, body) = impl val Template(parents, self, body) = impl
val newImpl = Template(parents, self, body ++ codeBlocks) val newImpl = Template(parents, self, body ++ codeBlocks)
ModuleDef(mods, name, newImpl) ModuleDef(mods, name, newImpl)
} }
/** /** Extract the internal fields of members belonging to the class, but not in primary constructor.
* Extract the internal fields of members belonging to the class, but not in primary constructor.
* *
* @param annotteeClassDefinitions * @param annotteeClassDefinitions
* @return Return a sequence of [[scala.reflect.api.Trees#ValDef]] * @return
* Return a sequence of [[scala.reflect.api.Trees#ValDef]]
*/ */
def getClassMemberValDefs(annotteeClassDefinitions: Seq[Tree]): Seq[ValDef] = def getClassMemberValDefs(annotteeClassDefinitions: Seq[Tree]): Seq[ValDef] =
annotteeClassDefinitions annotteeClassDefinitions
...@@ -199,29 +201,29 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) { ...@@ -199,29 +201,29 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) {
}) })
.map(_.asInstanceOf[ValDef]) .map(_.asInstanceOf[ValDef])
/** /** Extract the constructor params ValDef and flatten for currying.
* Extract the constructor params ValDef and flatten for currying.
* *
* @param annotteeClassParams * @param annotteeClassParams
* @return Return a sequence of [[scala.reflect.api.Trees#ValDef]] * @return
* Return a sequence of [[scala.reflect.api.Trees#ValDef]]
*/ */
def getClassConstructorValDefsFlatten(annotteeClassParams: List[List[Tree]]): Seq[ValDef] = def getClassConstructorValDefsFlatten(annotteeClassParams: List[List[Tree]]): Seq[ValDef] =
annotteeClassParams.flatten.map(_.asInstanceOf[ValDef]) annotteeClassParams.flatten.map(_.asInstanceOf[ValDef])
/** /** Extract the constructor params [[scala.reflect.api.Trees#ValDef]] not flatten.
* Extract the constructor params [[scala.reflect.api.Trees#ValDef]] not flatten.
* *
* @param annotteeClassParams * @param annotteeClassParams
* @return Return a double sequence of [[scala.reflect.api.Trees#ValDef]] * @return
* Return a double sequence of [[scala.reflect.api.Trees#ValDef]]
*/ */
def getClassConstructorValDefsNotFlatten(annotteeClassParams: List[List[Tree]]): Seq[Seq[ValDef]] = def getClassConstructorValDefsNotFlatten(annotteeClassParams: List[List[Tree]]): Seq[Seq[ValDef]] =
annotteeClassParams.map(_.map(_.asInstanceOf[ValDef])) annotteeClassParams.map(_.map(_.asInstanceOf[ValDef]))
/** /** Extract the methods belonging to the class, contains Secondary Constructor.
* Extract the methods belonging to the class, contains Secondary Constructor.
* *
* @param annotteeClassDefinitions * @param annotteeClassDefinitions
* @return Return a sequence of [[scala.reflect.api.Trees#DefDef]] * @return
* Return a sequence of [[scala.reflect.api.Trees#DefDef]]
*/ */
def getClassMemberDefDefs(annotteeClassDefinitions: Seq[Tree]): Seq[DefDef] = def getClassMemberDefDefs(annotteeClassDefinitions: Seq[Tree]): Seq[DefDef] =
annotteeClassDefinitions annotteeClassDefinitions
...@@ -231,22 +233,23 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) { ...@@ -231,22 +233,23 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) {
}) })
.map(_.asInstanceOf[DefDef]) .map(_.asInstanceOf[DefDef])
/** /** We generate constructor with currying, and we have to deal with the first layer of currying alone.
* We generate constructor with currying, and we have to deal with the first layer of currying alone.
* *
* @param typeName * @param typeName
* @param fieldss * @param fieldss
* @param isCase * @param isCase
* @return A constructor with currying, it not contains tpt, provide for calling method. * @return
* @example Return a tree, such as `new TestClass12(i)(j)(k)(t)` * A constructor with currying, it not contains tpt, provide for calling method.
* @example
* Return a tree, such as `new TestClass12(i)(j)(k)(t)`
*/ */
def getConstructorWithCurrying(typeName: TypeName, fieldss: List[List[Tree]], isCase: Boolean): Tree = { def getConstructorWithCurrying(typeName: TypeName, fieldss: List[List[Tree]], isCase: Boolean): Tree = {
val fieldssValDefNotFlatten = getClassConstructorValDefsNotFlatten(fieldss) val fieldssValDefNotFlatten = getClassConstructorValDefsNotFlatten(fieldss)
val allFieldsTermName = fieldssValDefNotFlatten.map(_.map(_.name.toTermName)) val allFieldsTermName = fieldssValDefNotFlatten.map(_.map(_.name.toTermName))
// not currying // not currying
val constructor = if (fieldss.isEmpty || fieldss.size == 1) { val constructor = if (fieldss.isEmpty || fieldss.size == 1) {
q"${if (isCase) q"${typeName.toTermName}(..${allFieldsTermName.flatten})" q"${if (isCase) q"${typeName.toTermName}(..${allFieldsTermName.flatten})"
else q"new $typeName(..${allFieldsTermName.flatten})"}" else q"new $typeName(..${allFieldsTermName.flatten})"}"
} else { } else {
// currying // currying
val first = allFieldsTermName.head val first = allFieldsTermName.head
...@@ -256,42 +259,43 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) { ...@@ -256,42 +259,43 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) {
constructor constructor
} }
/** /** Only for primitive types, we can get type and map to scala type.
* Only for primitive types, we can get type and map to scala type.
* *
* @param javaType java type name * @param javaType
* @return Scala type name * java type name
* @return
* Scala type name
*/ */
def toScalaType(javaType: String): String = { def toScalaType(javaType: String): String = {
val types = Map( val types = Map(
"java.lang.Integer" -> "Int", "java.lang.Integer" -> "Int",
"java.lang.Long" -> "Long", "java.lang.Long" -> "Long",
"java.lang.Double" -> "Double", "java.lang.Double" -> "Double",
"java.lang.Float" -> "Float", "java.lang.Float" -> "Float",
"java.lang.Short" -> "Short", "java.lang.Short" -> "Short",
"java.lang.Byte" -> "Byte", "java.lang.Byte" -> "Byte",
"java.lang.Boolean" -> "Boolean", "java.lang.Boolean" -> "Boolean",
"java.lang.Character" -> "Char", "java.lang.Character" -> "Char",
"java.lang.String" -> "String" "java.lang.String" -> "String"
) )
types.getOrElse(javaType, javaType) types.getOrElse(javaType, javaType)
} }
/** /** Gets a list of generic parameters. This is because the generic parameters of a class cannot be used directly in
* Gets a list of generic parameters. * the return type, and need to be converted.
* This is because the generic parameters of a class cannot be used directly in the return type, and need to be converted.
* *
* @param tpParams * @param tpParams
* @return Return a sequence of [[scala.reflect.api.Names#TypeName]] * @return
* Return a sequence of [[scala.reflect.api.Names#TypeName]]
*/ */
def extractClassTypeParamsTypeName(tpParams: List[Tree]): List[TypeName] = def extractClassTypeParamsTypeName(tpParams: List[Tree]): List[TypeName] =
tpParams.map(_.asInstanceOf[TypeDef].name) tpParams.map(_.asInstanceOf[TypeDef].name)
/** /** Is there a parent class? Does not contains sdk class, such as AnyRef and Object.
* Is there a parent class? Does not contains sdk class, such as AnyRef and Object.
* *
* @param superClasses * @param superClasses
* @return Return true if there is a non-SDK super class * @return
* Return true if there is a non-SDK super class
*/ */
def existsSuperClassExcludeSdkClass(superClasses: Seq[Tree]): Boolean = def existsSuperClassExcludeSdkClass(superClasses: Seq[Tree]): Boolean =
superClasses.nonEmpty && !superClasses.forall(sc => SDKClasses.contains(sc.toString())) superClasses.nonEmpty && !superClasses.forall(sc => SDKClasses.contains(sc.toString()))
...@@ -310,22 +314,25 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) { ...@@ -310,22 +314,25 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) {
def paramType: Type = c.typecheck(tq"$tpt", c.TYPEmode).tpe def paramType: Type = c.typecheck(tq"$tpt", c.TYPEmode).tpe
} }
/** /** Retrieves the accessor fields on a class and returns a Seq of
* Retrieves the accessor fields on a class and returns a Seq of [[org.bitlap.tools.macros.AbstractMacroProcessor#ValDefAccessor]]. * [[org.bitlap.tools.macros.AbstractMacroProcessor#ValDefAccessor]].
* *
* @param params The list of params retrieved from the class * @param params
* @return Return a sequence of [[org.bitlap.tools.macros.AbstractMacroProcessor#ValDefAccessor]] * The list of params retrieved from the class
* @return
* Return a sequence of [[org.bitlap.tools.macros.AbstractMacroProcessor#ValDefAccessor]]
*/ */
def valDefAccessors(params: Seq[Tree]): Seq[ValDefAccessor] = def valDefAccessors(params: Seq[Tree]): Seq[ValDefAccessor] =
params.map { case ValDef(mods, name: TermName, tpt: Tree, rhs) => params.map { case ValDef(mods, name: TermName, tpt: Tree, rhs) =>
ValDefAccessor(mods, name, tpt, rhs) ValDefAccessor(mods, name, tpt, rhs)
} }
/** /** Extract the necessary structure information of the class for macro programming.
* Extract the necessary structure information of the class for macro programming.
* *
* @param classDecl * @param classDecl
* @return Return the expansion of the class definition as [[org.bitlap.tools.macros.AbstractMacroProcessor#ClassDefinition]] * @return
* Return the expansion of the class definition as
* [[org.bitlap.tools.macros.AbstractMacroProcessor#ClassDefinition]]
*/ */
def mapToClassDeclInfo(classDecl: ClassDef): ClassDefinition = { def mapToClassDeclInfo(classDecl: ClassDef): ClassDefinition = {
val q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" = val q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" =
...@@ -343,11 +350,12 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) { ...@@ -343,11 +350,12 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) {
) )
} }
/** /** Extract the necessary structure information of the moduleDef for macro programming.
* Extract the necessary structure information of the moduleDef for macro programming.
* *
* @param moduleDef * @param moduleDef
* @return Return the expansion of the class definition as [[org.bitlap.tools.macros.AbstractMacroProcessor#ClassDefinition]] * @return
* Return the expansion of the class definition as
* [[org.bitlap.tools.macros.AbstractMacroProcessor#ClassDefinition]]
*/ */
def mapToModuleDeclInfo(moduleDef: ModuleDef): ClassDefinition = { def mapToModuleDeclInfo(moduleDef: ModuleDef): ClassDefinition = {
val q"$mods object $tpname extends { ..$earlydefns } with ..$parents { $self => ..$stats }" = moduleDef val q"$mods object $tpname extends { ..$earlydefns } with ..$parents { $self => ..$stats }" = moduleDef
...@@ -362,18 +370,19 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) { ...@@ -362,18 +370,19 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) {
) )
} }
/** /** Generate the specified syntax tree and assign it to the tree definition itself. Used only when you modify the
* Generate the specified syntax tree and assign it to the tree definition itself. * definition of the class itself. Such as add method/add field.
* Used only when you modify the definition of the class itself. Such as add method/add field.
* *
* @param classDecl * @param classDecl
* @param classInfoAction Content body added in class definition * @param classInfoAction
* @return Return a new [[scala.reflect.api.Trees#ClassDef]] * Content body added in class definition
* @return
* Return a new [[scala.reflect.api.Trees#ClassDef]]
*/ */
def appendClassBody(classDecl: ClassDef, classInfoAction: ClassDefinition => List[Tree]): c.universe.ClassDef = { def appendClassBody(classDecl: ClassDef, classInfoAction: ClassDefinition => List[Tree]): c.universe.ClassDef = {
val classInfo = mapToClassDeclInfo(classDecl) val classInfo = mapToClassDeclInfo(classDecl)
val ClassDef(mods, name, tparams, impl) = classDecl val ClassDef(mods, name, tparams, impl) = classDecl
val Template(parents, self, body) = impl val Template(parents, self, body) = impl
ClassDef(mods, name, tparams, Template(parents, self, body ++ classInfoAction(classInfo))) ClassDef(mods, name, tparams, Template(parents, self, body ++ classInfoAction(classInfo)))
} }
...@@ -404,12 +413,13 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) { ...@@ -404,12 +413,13 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) {
q"$mods object $tpname extends { ..$earlydefns } with ..${parents.toList ++ classInfoAction(classInfo)} { $self => ..$stats }" q"$mods object $tpname extends { ..$earlydefns } with ..${parents.toList ++ classInfoAction(classInfo)} { $self => ..$stats }"
} }
/** /** Modify the method body of the method tree.
* Modify the method body of the method tree.
* *
* @param defDef * @param defDef
* @param defBodyAction Method body of final result * @param defBodyAction
* @return Return a new [[scala.reflect.api.Trees#DefDef]] which changed by defBodyAction function * Method body of final result
* @return
* Return a new [[scala.reflect.api.Trees#DefDef]] which changed by defBodyAction function
*/ */
def mapToMethodDef(defDef: DefDef, defBodyAction: => Tree): c.universe.DefDef = { def mapToMethodDef(defDef: DefDef, defBodyAction: => Tree): c.universe.DefDef = {
val DefDef(mods, name, tparams, vparamss, tpt, rhs) = defDef val DefDef(mods, name, tparams, vparamss, tpt, rhs) = defDef
...@@ -427,11 +437,11 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) { ...@@ -427,11 +437,11 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) {
earlydefns: List[Tree] = Nil earlydefns: List[Tree] = Nil
) )
/** /** Find the specified val Name in the enclosingClass definition.
* Find the specified val Name in the enclosingClass definition.
* *
* @param t * @param t
* @return Return a optional [[scala.reflect.api.Names#TermName]] * @return
* Return a optional [[scala.reflect.api.Names#TermName]]
*/ */
def findValDefInEnclosingClass(t: Name): Option[TermName] = { def findValDefInEnclosingClass(t: Name): Option[TermName] = {
@tailrec @tailrec
...@@ -440,7 +450,7 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) { ...@@ -440,7 +450,7 @@ abstract class AbstractMacroProcessor(val c: whitebox.Context) {
case tree :: tail => case tree :: tail =>
tree match { tree match {
case ValDef(_, name, tpt, _) => case ValDef(_, name, tpt, _) =>
if (c.typecheck(tq"$tpt", c.TYPEmode).tpe.toString == t.decodedName.toString) //TODO better if (c.typecheck(tq"$tpt", c.TYPEmode).tpe.toString == t.decodedName.toString) // TODO better
Some(name.toTermName) Some(name.toTermName)
else None else None
case _ => doFind(tail) case _ => doFind(tail)
......
...@@ -23,10 +23,10 @@ package org.bitlap.tools.macros ...@@ -23,10 +23,10 @@ package org.bitlap.tools.macros
import scala.reflect.macros.whitebox import scala.reflect.macros.whitebox
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2021/7/7 * @since 2021/7/7
* @version 1.0 * @version 1.0
*/ */
object applyMacro { object applyMacro {
...@@ -34,13 +34,15 @@ object applyMacro { ...@@ -34,13 +34,15 @@ object applyMacro {
import c.universe._ import c.universe._
/** /** We generate apply method with currying, and we have to deal with the first layer of currying alone.
* We generate apply method with currying, and we have to deal with the first layer of currying alone.
* *
* @param typeName * @param typeName
* @param fieldss * @param fieldss
* @return A apply method with currying. * @return
* @example Return a tree, such as `def apply(int: Int)(j: Int)(k: Option[String])(t: Option[Long]): B3 = new B3(int)(j)(k)(t)` * A apply method with currying.
* @example
* Return a tree, such as `def apply(int: Int)(j: Int)(k: Option[String])(t: Option[Long]): B3 = new
* B3(int)(j)(k)(t)`
*/ */
private def getApplyMethodWithCurrying( private def getApplyMethodWithCurrying(
typeName: TypeName, typeName: TypeName,
...@@ -48,7 +50,7 @@ object applyMacro { ...@@ -48,7 +50,7 @@ object applyMacro {
classTypeParams: List[Tree] classTypeParams: List[Tree]
): Tree = { ): Tree = {
val allFieldsTermName = fieldss.map(f => getConstructorParamsNameWithType(f)) val allFieldsTermName = fieldss.map(f => getConstructorParamsNameWithType(f))
val returnTypeParams = extractClassTypeParamsTypeName(classTypeParams) val returnTypeParams = extractClassTypeParamsTypeName(classTypeParams)
// not currying // not currying
val applyMethod = if (fieldss.isEmpty || fieldss.size == 1) { val applyMethod = if (fieldss.isEmpty || fieldss.size == 1) {
q"def apply[..$classTypeParams](..${allFieldsTermName.flatten}): $typeName[..$returnTypeParams] = ${getConstructorWithCurrying(typeName, fieldss, isCase = false)}" q"def apply[..$classTypeParams](..${allFieldsTermName.flatten}): $typeName[..$returnTypeParams] = ${getConstructorWithCurrying(typeName, fieldss, isCase = false)}"
......
...@@ -23,10 +23,10 @@ package org.bitlap.tools.macros ...@@ -23,10 +23,10 @@ package org.bitlap.tools.macros
import scala.reflect.macros.whitebox import scala.reflect.macros.whitebox
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2021/7/7 * @since 2021/7/7
* @version 1.0 * @version 1.0
*/ */
object builderMacro { object builderMacro {
...@@ -45,14 +45,12 @@ object builderMacro { ...@@ -45,14 +45,12 @@ object builderMacro {
private def getFieldSetMethod(typeName: TypeName, field: Tree, classTypeParams: List[Tree]): Tree = { private def getFieldSetMethod(typeName: TypeName, field: Tree, classTypeParams: List[Tree]): Tree = {
val builderClassName = getBuilderClassName(typeName) val builderClassName = getBuilderClassName(typeName)
val returnTypeParams = extractClassTypeParamsTypeName(classTypeParams) val returnTypeParams = extractClassTypeParamsTypeName(classTypeParams)
lazy val valDefMapTo = (v: ValDef) => { lazy val valDefMapTo = (v: ValDef) => q"""
q"""
def ${v.name}(${v.name}: ${v.tpt}): $builderClassName[..$returnTypeParams] = { def ${v.name}(${v.name}: ${v.tpt}): $builderClassName[..$returnTypeParams] = {
this.${v.name} = ${v.name} this.${v.name} = ${v.name}
this this
} }
""" """
}
valDefMapTo(field.asInstanceOf[ValDef]) valDefMapTo(field.asInstanceOf[ValDef])
} }
...@@ -62,11 +60,11 @@ object builderMacro { ...@@ -62,11 +60,11 @@ object builderMacro {
classTypeParams: List[Tree], classTypeParams: List[Tree],
isCase: Boolean isCase: Boolean
): List[Tree] = { ): List[Tree] = {
val fields = fieldss.flatten val fields = fieldss.flatten
val builderClassName = getBuilderClassName(typeName) val builderClassName = getBuilderClassName(typeName)
val builderFieldMethods = fields.map(f => getFieldSetMethod(typeName, f, classTypeParams)) val builderFieldMethods = fields.map(f => getFieldSetMethod(typeName, f, classTypeParams))
val builderFieldDefinitions = fields.map(f => getFieldDefinition(f)) val builderFieldDefinitions = fields.map(f => getFieldDefinition(f))
val returnTypeParams = extractClassTypeParamsTypeName(classTypeParams) val returnTypeParams = extractClassTypeParamsTypeName(classTypeParams)
val builderMethod = val builderMethod =
q"def builder[..$classTypeParams](): $builderClassName[..$returnTypeParams] = new $builderClassName()" q"def builder[..$classTypeParams](): $builderClassName[..$returnTypeParams] = new $builderClassName()"
val buulderClass = val buulderClass =
......
...@@ -23,10 +23,10 @@ package org.bitlap.tools.macros ...@@ -23,10 +23,10 @@ package org.bitlap.tools.macros
import scala.reflect.macros.whitebox import scala.reflect.macros.whitebox
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2021/7/7 * @since 2021/7/7
* @version 1.0 * @version 1.0
*/ */
object constructorMacro { object constructorMacro {
...@@ -34,23 +34,21 @@ object constructorMacro { ...@@ -34,23 +34,21 @@ object constructorMacro {
import c.universe._ import c.universe._
private val extractArgs: Seq[String] = { private val extractArgs: Seq[String] =
c.prefix.tree match { c.prefix.tree match {
case q"new constructor(excludeFields=$excludeFields)" => evalTree(excludeFields.asInstanceOf[Tree]) case q"new constructor(excludeFields=$excludeFields)" => evalTree(excludeFields.asInstanceOf[Tree])
case q"new constructor($excludeFields)" => evalTree(excludeFields.asInstanceOf[Tree]) case q"new constructor($excludeFields)" => evalTree(excludeFields.asInstanceOf[Tree])
case q"new constructor()" => Seq.empty[String] case q"new constructor()" => Seq.empty[String]
case _ => c.abort(c.enclosingPosition, ErrorMessage.UNEXPECTED_PATTERN) case _ => c.abort(c.enclosingPosition, ErrorMessage.UNEXPECTED_PATTERN)
} }
}
private def getMutableValDefAndExcludeFields(annotteeClassDefinitions: Seq[Tree]): Seq[c.universe.ValDef] = private def getMutableValDefAndExcludeFields(annotteeClassDefinitions: Seq[Tree]): Seq[c.universe.ValDef] =
getClassMemberValDefs(annotteeClassDefinitions).filter(v => getClassMemberValDefs(annotteeClassDefinitions).filter(v =>
v.mods.hasFlag(Flag.MUTABLE) && v.mods.hasFlag(Flag.MUTABLE) &&
!extractArgs.contains(v.name.decodedName.toString) !extractArgs.contains(v.name.decodedName.toString)
) )
/** /** Extract the internal fields of members belonging to the class, but not in primary constructor and only `var`.
* Extract the internal fields of members belonging to the class, but not in primary constructor and only `var`.
*/ */
private def getMemberVarDefTermNameWithType(annotteeClassDefinitions: Seq[Tree]): Seq[Tree] = private def getMemberVarDefTermNameWithType(annotteeClassDefinitions: Seq[Tree]): Seq[Tree] =
getMutableValDefAndExcludeFields(annotteeClassDefinitions).map { v => getMutableValDefAndExcludeFields(annotteeClassDefinitions).map { v =>
...@@ -62,8 +60,7 @@ object constructorMacro { ...@@ -62,8 +60,7 @@ object constructorMacro {
} }
} }
/** /** We generate this method with currying, and we have to deal with the first layer of currying alone.
* We generate this method with currying, and we have to deal with the first layer of currying alone.
*/ */
private def getThisMethodWithCurrying( private def getThisMethodWithCurrying(
annotteeClassParams: List[List[Tree]], annotteeClassParams: List[List[Tree]],
......
...@@ -28,13 +28,14 @@ import scala.concurrent.duration._ ...@@ -28,13 +28,14 @@ import scala.concurrent.duration._
import scala.reflect.macros.whitebox import scala.reflect.macros.whitebox
/** /**
* 1. This annotation only support use on non-abstract method. * 1. This annotation only support use on non-abstract method. 2. For methods that are not future, `try finally` is
* 2. For methods that are not future, `try finally` is used to implement the timing of the method. * used to implement the timing of the method. 3. For methods that are Futures, `Future map` is used to implement
* 3. For methods that are Futures, `Future map` is used to implement the timing of the method. * the timing of the method.
* *
* @author 梦境迷离 * @author
* @since 2021/8/7 * 梦境迷离
* @version 1.0 * @since 2021/8/7
* @version 1.0
*/ */
object elapsedMacro { object elapsedMacro {
...@@ -42,7 +43,7 @@ object elapsedMacro { ...@@ -42,7 +43,7 @@ object elapsedMacro {
import c.universe._ import c.universe._
private lazy val start: c.universe.TermName = TermName("$elapsedBegin") private lazy val start: c.universe.TermName = TermName("$elapsedBegin")
private lazy val valDef: c.universe.TermName = TermName("$elapsed") private lazy val valDef: c.universe.TermName = TermName("$elapsed")
private def getLogLevel(logLevel: Tree): LogLevel = private def getLogLevel(logLevel: Tree): LogLevel =
...@@ -99,10 +100,13 @@ object elapsedMacro { ...@@ -99,10 +100,13 @@ object elapsedMacro {
private def getNewMethodWithFuture(defDef: DefDef): DefDef = private def getNewMethodWithFuture(defDef: DefDef): DefDef =
mapToNewMethod( mapToNewMethod(
defDef, defDef,
defDef => q""" defDef =>
q"""
$getStartExpr $getStartExpr
val resFuture = ${defDef.rhs} val resFuture = ${defDef.rhs}
resFuture.map { res => ..${getPrintlnLog(defDef.name)} ; res }(_root_.scala.concurrent.ExecutionContext.Implicits.global) resFuture.map { res => ..${getPrintlnLog(
defDef.name
)} ; res }(_root_.scala.concurrent.ExecutionContext.Implicits.global)
""" """
) )
......
...@@ -23,10 +23,10 @@ package org.bitlap.tools.macros ...@@ -23,10 +23,10 @@ package org.bitlap.tools.macros
import scala.reflect.macros.whitebox import scala.reflect.macros.whitebox
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2021/7/18 * @since 2021/7/18
* @version 1.0 * @version 1.0
*/ */
object equalsAndHashCodeMacro { object equalsAndHashCodeMacro {
...@@ -38,7 +38,7 @@ object equalsAndHashCodeMacro { ...@@ -38,7 +38,7 @@ object equalsAndHashCodeMacro {
case q"new equalsAndHashCode(excludeFields=$excludeFields)" => evalTree(excludeFields.asInstanceOf[Tree]) case q"new equalsAndHashCode(excludeFields=$excludeFields)" => evalTree(excludeFields.asInstanceOf[Tree])
case q"new equalsAndHashCode($excludeFields)" => evalTree(excludeFields.asInstanceOf[Tree]) case q"new equalsAndHashCode($excludeFields)" => evalTree(excludeFields.asInstanceOf[Tree])
case q"new equalsAndHashCode()" => Nil case q"new equalsAndHashCode()" => Nil
case _ => c.abort(c.enclosingPosition, ErrorMessage.UNEXPECTED_PATTERN) case _ => c.abort(c.enclosingPosition, ErrorMessage.UNEXPECTED_PATTERN)
} }
override def checkAnnottees(annottees: Seq[c.universe.Expr[Any]]): Unit = { override def checkAnnottees(annottees: Seq[c.universe.Expr[Any]]): Unit = {
...@@ -49,8 +49,7 @@ object equalsAndHashCodeMacro { ...@@ -49,8 +49,7 @@ object equalsAndHashCodeMacro {
} }
} }
/** /** Extract the internal fields of members belonging to the class.
* Extract the internal fields of members belonging to the class.
*/ */
private def getInternalFieldsTermNameExcludeLocal(annotteeClassDefinitions: Seq[Tree]): Seq[TermName] = { private def getInternalFieldsTermNameExcludeLocal(annotteeClassDefinitions: Seq[Tree]): Seq[TermName] = {
if (annotteeClassDefinitions.exists(f => isNotLocalClassMember(f))) { if (annotteeClassDefinitions.exists(f => isNotLocalClassMember(f))) {
...@@ -59,7 +58,7 @@ object equalsAndHashCodeMacro { ...@@ -59,7 +58,7 @@ object equalsAndHashCodeMacro {
getClassMemberValDefs(annotteeClassDefinitions) getClassMemberValDefs(annotteeClassDefinitions)
.filter(p => .filter(p =>
isNotLocalClassMember(p) && isNotLocalClassMember(p) &&
!extractArgs.contains(p.name.decodedName.toString) !extractArgs.contains(p.name.decodedName.toString)
) )
.map(_.name.toTermName) .map(_.name.toTermName)
} }
...@@ -88,9 +87,9 @@ object equalsAndHashCodeMacro { ...@@ -88,9 +87,9 @@ object equalsAndHashCodeMacro {
override def equals(that: Any): Boolean = override def equals(that: Any): Boolean =
that match { that match {
case t: $className => t.canEqual(this) && Seq(..$equalsExprs).forall(f => f) && ${if ( case t: $className => t.canEqual(this) && Seq(..$equalsExprs).forall(f => f) && ${if (
existsSuperClassExcludeSdkClass(superClasses) existsSuperClassExcludeSdkClass(superClasses)
) q"super.equals(that)" ) q"super.equals(that)"
else q"true"} else q"true"}
case _ => false case _ => false
} }
""" """
...@@ -105,20 +104,19 @@ object equalsAndHashCodeMacro { ...@@ -105,20 +104,19 @@ object equalsAndHashCodeMacro {
override def hashCode(): Int = { override def hashCode(): Int = {
val state = Seq(..$termNames) val state = Seq(..$termNames)
state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b) + ${if ( state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b) + ${if (
existsSuperClassExcludeSdkClass(superClasses) existsSuperClassExcludeSdkClass(superClasses)
) superTree ) superTree
else q"0"} else q"0"}
} }
""" """
} }
override def createCustomExpr(classDecl: ClassDef, compDeclOpt: Option[ModuleDef]): Any = { override def createCustomExpr(classDecl: ClassDef, compDeclOpt: Option[ModuleDef]): Any = {
lazy val map = (classDefinition: ClassDefinition) => { lazy val map = (classDefinition: ClassDefinition) =>
getClassConstructorValDefsFlatten(classDefinition.classParamss) getClassConstructorValDefsFlatten(classDefinition.classParamss)
.filter(cf => isNotLocalClassMember(cf)) .filter(cf => isNotLocalClassMember(cf))
.map(_.name.toTermName) ++ .map(_.name.toTermName) ++
getInternalFieldsTermNameExcludeLocal(classDefinition.body) getInternalFieldsTermNameExcludeLocal(classDefinition.body)
}
val classDefinition = mapToClassDeclInfo(classDecl) val classDefinition = mapToClassDeclInfo(classDecl)
val res = appendClassBody( val res = appendClassBody(
classDecl, classDecl,
......
...@@ -29,14 +29,13 @@ object jacksonEnumMacro { ...@@ -29,14 +29,13 @@ object jacksonEnumMacro {
import c.universe._ import c.universe._
private val extractArgs: Seq[String] = { private val extractArgs: Seq[String] =
c.prefix.tree match { c.prefix.tree match {
case q"new jacksonEnum(nonTypeRefers=$nonTypeRefers)" => evalTree(nonTypeRefers.asInstanceOf[Tree]) case q"new jacksonEnum(nonTypeRefers=$nonTypeRefers)" => evalTree(nonTypeRefers.asInstanceOf[Tree])
case q"new jacksonEnum($nonTypeRefers)" => evalTree(nonTypeRefers.asInstanceOf[Tree]) case q"new jacksonEnum($nonTypeRefers)" => evalTree(nonTypeRefers.asInstanceOf[Tree])
case q"new jacksonEnum()" => Nil case q"new jacksonEnum()" => Nil
case _ => c.abort(c.enclosingPosition, ErrorMessage.UNEXPECTED_PATTERN) case _ => c.abort(c.enclosingPosition, ErrorMessage.UNEXPECTED_PATTERN)
} }
}
private def getJacksonTypeReferClasses(valDefs: List[ValDef]): Seq[Tree] = { private def getJacksonTypeReferClasses(valDefs: List[ValDef]): Seq[Tree] = {
val safeValDefs = valDefAccessors(valDefs) val safeValDefs = valDefAccessors(valDefs)
...@@ -48,8 +47,8 @@ object jacksonEnumMacro { ...@@ -48,8 +47,8 @@ object jacksonEnumMacro {
.distinct .distinct
.map(c => .map(c =>
q"""class ${TypeName( q"""class ${TypeName(
c.decodedName.toString + "TypeRefer" c.decodedName.toString + "TypeRefer"
)} extends _root_.com.fasterxml.jackson.core.`type`.TypeReference[$c.type]""" )} extends _root_.com.fasterxml.jackson.core.`type`.TypeReference[$c.type]"""
) )
} }
...@@ -85,12 +84,12 @@ object jacksonEnumMacro { ...@@ -85,12 +84,12 @@ object jacksonEnumMacro {
override def createCustomExpr(classDecl: c.universe.ClassDef, compDeclOpt: Option[c.universe.ModuleDef]): Any = { override def createCustomExpr(classDecl: c.universe.ClassDef, compDeclOpt: Option[c.universe.ModuleDef]): Any = {
// return all typeReferClasses and new classDef // return all typeReferClasses and new classDef
val classDefinition = mapToClassDeclInfo(classDecl) val classDefinition = mapToClassDeclInfo(classDecl)
val valDefs = classDefinition.classParamss.flatten.map(_.asInstanceOf[ValDef]) val valDefs = classDefinition.classParamss.flatten.map(_.asInstanceOf[ValDef])
val typeReferClasses = getJacksonTypeReferClasses(valDefs).distinct val typeReferClasses = getJacksonTypeReferClasses(valDefs).distinct
val q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends ..$bases { ..$body }" = classDecl val q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends ..$bases { ..$body }" = classDecl
val newFieldss = paramss.asInstanceOf[List[List[Tree]]].map(_.map(replaceAnnotation)) val newFieldss = paramss.asInstanceOf[List[List[Tree]]].map(_.map(replaceAnnotation))
val newClass = q"$mods class $tpname[..$tparams] $ctorMods(...$newFieldss) extends ..$bases { ..$body }" val newClass = q"$mods class $tpname[..$tparams] $ctorMods(...$newFieldss) extends ..$bases { ..$body }"
val res = val res =
q""" q"""
..$typeReferClasses ..$typeReferClasses
......
...@@ -23,10 +23,10 @@ package org.bitlap.tools.macros ...@@ -23,10 +23,10 @@ package org.bitlap.tools.macros
import scala.reflect.macros.whitebox import scala.reflect.macros.whitebox
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2021/11/23 * @since 2021/11/23
* @version 1.0 * @version 1.0
*/ */
object javaCompatibleMacro { object javaCompatibleMacro {
...@@ -34,8 +34,7 @@ object javaCompatibleMacro { ...@@ -34,8 +34,7 @@ object javaCompatibleMacro {
import c.universe._ import c.universe._
/** /** We generate this method with currying, and we have to deal with the first layer of currying alone.
* We generate this method with currying, and we have to deal with the first layer of currying alone.
*/ */
private def getNoArgsContrWithCurrying( private def getNoArgsContrWithCurrying(
annotteeClassParams: List[List[Tree]], annotteeClassParams: List[List[Tree]],
......
...@@ -23,10 +23,10 @@ package org.bitlap.tools.macros ...@@ -23,10 +23,10 @@ package org.bitlap.tools.macros
import scala.reflect.macros.whitebox import scala.reflect.macros.whitebox
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2021/7/7 * @since 2021/7/7
* @version 1.0 * @version 1.0
*/ */
object jsonMacro { object jsonMacro {
...@@ -51,8 +51,8 @@ object jsonMacro { ...@@ -51,8 +51,8 @@ object jsonMacro {
override def createCustomExpr(classDecl: c.universe.ClassDef, compDeclOpt: Option[c.universe.ModuleDef]): Any = { override def createCustomExpr(classDecl: c.universe.ClassDef, compDeclOpt: Option[c.universe.ModuleDef]): Any = {
val classDefinition = mapToClassDeclInfo(classDecl) val classDefinition = mapToClassDeclInfo(classDecl)
val format = jsonFormatter(classDefinition.className, classDefinition.classParamss.flatten) val format = jsonFormatter(classDefinition.className, classDefinition.classParamss.flatten)
val compDecl = appendModuleBody(compDeclOpt, List(format), classDefinition.className) val compDecl = appendModuleBody(compDeclOpt, List(format), classDefinition.className)
// Return both the class and companion object declarations // Return both the class and companion object declarations
c.Expr(q""" c.Expr(q"""
$classDecl $classDecl
......
...@@ -27,10 +27,10 @@ import org.bitlap.tools.{ logs, PACKAGE } ...@@ -27,10 +27,10 @@ import org.bitlap.tools.{ logs, PACKAGE }
import scala.reflect.macros.whitebox import scala.reflect.macros.whitebox
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2021/7/7 * @since 2021/7/7
* @version 1.0 * @version 1.0
*/ */
object logMacro { object logMacro {
......
...@@ -21,18 +21,18 @@ ...@@ -21,18 +21,18 @@
package org.bitlap.tools package org.bitlap.tools
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2021/7/7 * @since 2021/7/7
* @version 1.0 * @version 1.0
*/ */
package object macros { package object macros {
private[macros] object ErrorMessage { private[macros] object ErrorMessage {
// common error msg // common error msg
final lazy val ONLY_CLASS: String = "Annotation is only supported on class." final lazy val ONLY_CLASS: String = "Annotation is only supported on class."
final lazy val ONLY_CASE_CLASS: String = "Annotation is only supported on case class." final lazy val ONLY_CASE_CLASS: String = "Annotation is only supported on case class."
final lazy val ONLY_OBJECT_CLASS: String = "Annotation is only supported on class or object." final lazy val ONLY_OBJECT_CLASS: String = "Annotation is only supported on class or object."
final lazy val UNEXPECTED_PATTERN: String = "Unexpected annotation pattern!" final lazy val UNEXPECTED_PATTERN: String = "Unexpected annotation pattern!"
} }
......
...@@ -23,10 +23,10 @@ package org.bitlap.tools.macros ...@@ -23,10 +23,10 @@ package org.bitlap.tools.macros
import scala.reflect.macros.whitebox import scala.reflect.macros.whitebox
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2021/7/7 * @since 2021/7/7
* @version 1.0 * @version 1.0
*/ */
object toStringMacro { object toStringMacro {
...@@ -109,14 +109,14 @@ object toStringMacro { ...@@ -109,14 +109,14 @@ object toStringMacro {
}) })
val ctorParams = classDefinition.classParamss.flatten val ctorParams = classDefinition.classParamss.flatten
val member = if (argument.includeInternalFields) ctorParams ++ annotteeClassFieldDefinitions else ctorParams val member = if (argument.includeInternalFields) ctorParams ++ annotteeClassFieldDefinitions else ctorParams
val lastParam = member.lastOption.map { val lastParam = member.lastOption.map {
case v: ValDef => v.name.toTermName.decodedName.toString case v: ValDef => v.name.toTermName.decodedName.toString
case c => c.toString case c => c.toString
} }
val paramsWithName = member.foldLeft(q"${""}")((res, acc) => q"$res + ${printField(argument, lastParam, acc)}") val paramsWithName = member.foldLeft(q"${""}")((res, acc) => q"$res + ${printField(argument, lastParam, acc)}")
//scala/bug https://github.com/scala/bug/issues/3967 not be 'Foo(i=1,j=2)' in standard library // scala/bug https://github.com/scala/bug/issues/3967 not be 'Foo(i=1,j=2)' in standard library
val toString = val toString =
q"""override def toString: String = ${classDefinition.className.toTermName.decodedName.toString} + ${"("} + $paramsWithName + ${")"}""" q"""override def toString: String = ${classDefinition.className.toTermName.decodedName.toString} + ${"("} + $paramsWithName + ${")"}"""
...@@ -129,7 +129,7 @@ object toStringMacro { ...@@ -129,7 +129,7 @@ object toStringMacro {
superClassDef.fold(toString) { _ => superClassDef.fold(toString) { _ =>
val superClass = q"${"super="}" val superClass = q"${"super="}"
q"override def toString: String = StringContext(${classDefinition.className.toTermName.decodedName.toString} + ${"("} + $superClass, ${if (member.nonEmpty) ", " q"override def toString: String = StringContext(${classDefinition.className.toTermName.decodedName.toString} + ${"("} + $superClass, ${if (member.nonEmpty) ", "
else ""}+$paramsWithName + ${")"}).s(super.toString)" else ""}+$paramsWithName + ${")"}).s(super.toString)"
} }
} else { } else {
toString toString
......
...@@ -25,29 +25,41 @@ import org.bitlap.tools.methods.impl.ProcessorCreatorMacro ...@@ -25,29 +25,41 @@ import org.bitlap.tools.methods.impl.ProcessorCreatorMacro
import java.util.concurrent.Executor import java.util.concurrent.Executor
/** /** The macro util to generate processor for alipay sofa jraft rpc.
* The macro util to generate processor for alipay sofa jraft rpc.
* *
* @author 梦境迷离 * @author
* @version 1.0,2021/12/6 * 梦境迷离
* @version 1.0,2021/12/6
*/ */
@deprecated @deprecated
object ProcessorCreator { object ProcessorCreator {
/** /** @param service
* @param service Instance of the `Service` * Instance of the `Service`
* @param defaultResp Default instance of the Response Message * @param defaultResp
* @param executor Instance of the Executor * Default instance of the Response Message
* @param processRequest Function to handle request * @param executor
* @param processException Function to handle exception * Instance of the Executor
* @tparam RRC `RpcRequestClosure` * @param processRequest
* @tparam RRP `RpcRequestProcessor` * Function to handle request
* @tparam RC `RpcContext` * @param processException
* @tparam Req Request Message of the protobuf * Function to handle exception
* @tparam Resp Response Message of the protobuf * @tparam RRC
* @tparam Service Should be custom class/interface/trait which handle the business logic of Processors * `RpcRequestClosure`
* @tparam E Should be subclass of the Executor * @tparam RRP
* @return [[ RRP ]] Instance of the `RpcRequestProcessor` subclass * `RpcRequestProcessor`
* @tparam RC
* `RpcContext`
* @tparam Req
* Request Message of the protobuf
* @tparam Resp
* Response Message of the protobuf
* @tparam Service
* Should be custom class/interface/trait which handle the business logic of Processors
* @tparam E
* Should be subclass of the Executor
* @return
* [[RRP]] Instance of the `RpcRequestProcessor` subclass
*/ */
def apply[RRC, RRP[_ <: Req], RC, Req, Resp, Service, E <: Executor]( def apply[RRC, RRP[_ <: Req], RC, Req, Resp, Service, E <: Executor](
defaultResp: Resp, defaultResp: Resp,
...@@ -62,8 +74,8 @@ object ProcessorCreator { ...@@ -62,8 +74,8 @@ object ProcessorCreator {
)(implicit service: Service): RRP[Req] = )(implicit service: Service): RRP[Req] =
macro ProcessorCreatorMacro.WithoutExecutorAndDefaultResp[RRC, RRP[_ <: Req], RC, Req, Resp, Service] macro ProcessorCreatorMacro.WithoutExecutorAndDefaultResp[RRC, RRP[_ <: Req], RC, Req, Resp, Service]
/** /** Having two identical type parameters will cause the compiler to recognize error and change the order of generics
* Having two identical type parameters will cause the compiler to recognize error and change the order of generics to avoid. * to avoid.
*/ */
def apply[Service, RRC, RRP[_ <: Req], RC, Req, Resp](processRequest: (Service, RRC, Req) => Resp)( def apply[Service, RRC, RRP[_ <: Req], RC, Req, Resp](processRequest: (Service, RRC, Req) => Resp)(
processException: (Service, RC, Exception) => Resp processException: (Service, RC, Exception) => Resp
......
...@@ -21,10 +21,10 @@ ...@@ -21,10 +21,10 @@
package org.bitlap.tools.methods.impl package org.bitlap.tools.methods.impl
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2022/3/17 * @since 2022/3/17
* @version 1.0 * @version 1.0
*/ */
object MacroCache { object MacroCache {
......
...@@ -25,9 +25,9 @@ import java.time.ZonedDateTime ...@@ -25,9 +25,9 @@ import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import scala.reflect.macros.blackbox import scala.reflect.macros.blackbox
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @version 1.0,2021/12/6 * @version 1.0,2021/12/6
*/ */
object ProcessorCreatorMacro { object ProcessorCreatorMacro {
...@@ -48,12 +48,12 @@ object ProcessorCreatorMacro { ...@@ -48,12 +48,12 @@ object ProcessorCreatorMacro {
)(service: c.Expr[Service], executor: c.Expr[E]): c.Expr[RRP] = { // parameters in order, parameter names differ will compile error )(service: c.Expr[Service], executor: c.Expr[E]): c.Expr[RRP] = { // parameters in order, parameter names differ will compile error
import c.universe._ import c.universe._
checkTree[RRC, RRP, RC, Service](c)(needCheckService = false) checkTree[RRC, RRP, RC, Service](c)(needCheckService = false)
val serviceType = weakTypeOf[Service] val serviceType = weakTypeOf[Service]
val className = TypeName(classNamePrefix + MacroCache.getIdentityId) val className = TypeName(classNamePrefix + MacroCache.getIdentityId)
val reqProtoType = weakTypeOf[Req] val reqProtoType = weakTypeOf[Req]
val rpcRequestClosureType = weakTypeOf[RRC] val rpcRequestClosureType = weakTypeOf[RRC]
val rpcContextType = weakTypeOf[RC] val rpcContextType = weakTypeOf[RC]
val respProtoType = weakTypeOf[Resp] val respProtoType = weakTypeOf[Resp]
val processor = val processor =
q""" q"""
class $className(private val service: $serviceType, executor: java.util.concurrent.Executor = null) class $className(private val service: $serviceType, executor: java.util.concurrent.Executor = null)
...@@ -98,13 +98,13 @@ object ProcessorCreatorMacro { ...@@ -98,13 +98,13 @@ object ProcessorCreatorMacro {
)(service: c.Expr[Service]): c.Expr[RRP] = { )(service: c.Expr[Service]): c.Expr[RRP] = {
import c.universe._ import c.universe._
checkTree[RRC, RRP, RC, Service](c)(needCheckService = false) checkTree[RRC, RRP, RC, Service](c)(needCheckService = false)
val serviceType = weakTypeOf[Service] val serviceType = weakTypeOf[Service]
val className = TypeName(classNamePrefix + MacroCache.getIdentityId) val className = TypeName(classNamePrefix + MacroCache.getIdentityId)
val reqProtoType = weakTypeOf[Req] val reqProtoType = weakTypeOf[Req]
val rpcRequestClosureType = weakTypeOf[RRC] val rpcRequestClosureType = weakTypeOf[RRC]
val rpcContextType = weakTypeOf[RC] val rpcContextType = weakTypeOf[RC]
val respProtoType = weakTypeOf[Resp] val respProtoType = weakTypeOf[Resp]
val respProtoCompanionType = weakTypeOf[Resp].companion //getDefaultInstance is static method, it's in companion val respProtoCompanionType = weakTypeOf[Resp].companion // getDefaultInstance is static method, it's in companion
val processor = val processor =
q""" q"""
class $className(private val service: $serviceType, executor: java.util.concurrent.Executor = null) class $className(private val service: $serviceType, executor: java.util.concurrent.Executor = null)
...@@ -149,13 +149,13 @@ object ProcessorCreatorMacro { ...@@ -149,13 +149,13 @@ object ProcessorCreatorMacro {
): c.Expr[RRP] = { ): c.Expr[RRP] = {
import c.universe._ import c.universe._
checkTree[RRC, RRP, RC, Service](c)(needCheckService = true) checkTree[RRC, RRP, RC, Service](c)(needCheckService = true)
val serviceType = weakTypeOf[Service] val serviceType = weakTypeOf[Service]
val className = TypeName(classNamePrefix + MacroCache.getIdentityId) val className = TypeName(classNamePrefix + MacroCache.getIdentityId)
val reqProtoType = weakTypeOf[Req] val reqProtoType = weakTypeOf[Req]
val rpcRequestClosureType = weakTypeOf[RRC] val rpcRequestClosureType = weakTypeOf[RRC]
val rpcContextType = weakTypeOf[RC] val rpcContextType = weakTypeOf[RC]
val respProtoType = weakTypeOf[Resp] val respProtoType = weakTypeOf[Resp]
val respProtoCompanionType = weakTypeOf[Resp].companion //getDefaultInstance is static method, it's in companion val respProtoCompanionType = weakTypeOf[Resp].companion // getDefaultInstance is static method, it's in companion
val processor = val processor =
q""" q"""
class $className(private val service: $serviceType, executor: java.util.concurrent.Executor = null) class $className(private val service: $serviceType, executor: java.util.concurrent.Executor = null)
......
...@@ -21,10 +21,10 @@ ...@@ -21,10 +21,10 @@
package org.bitlap package org.bitlap
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2021/7/7 * @since 2021/7/7
* @version 1.0 * @version 1.0
*/ */
package object tools { package object tools {
final val PACKAGE: String = "org.bitlap.tools" final val PACKAGE: String = "org.bitlap.tools"
......
...@@ -25,15 +25,18 @@ import org.bitlap.tools.macros.toStringMacro ...@@ -25,15 +25,18 @@ import org.bitlap.tools.macros.toStringMacro
import scala.annotation.{ compileTimeOnly, StaticAnnotation } import scala.annotation.{ compileTimeOnly, StaticAnnotation }
/** /** annotation to generate toString for classes.
* annotation to generate toString for classes.
* *
* @author 梦境迷离 * @author
* @param includeInternalFields Whether to include the fields defined within a class. * 梦境迷离
* @param includeFieldNames Whether to include the name of the field in the toString. * @param includeInternalFields
* @param callSuper Whether to include the super's toString. * Whether to include the fields defined within a class.
* @since 2021/6/13 * @param includeFieldNames
* @version 1.0 * Whether to include the name of the field in the toString.
* @param callSuper
* Whether to include the super's toString.
* @since 2021/6/13
* @version 1.0
*/ */
@compileTimeOnly("enable macro to expand macro annotations") @compileTimeOnly("enable macro to expand macro annotations")
final class toString( final class toString(
......
...@@ -24,12 +24,12 @@ package org.bitlap.tools.utils ...@@ -24,12 +24,12 @@ package org.bitlap.tools.utils
import scala.reflect.runtime.currentMirror import scala.reflect.runtime.currentMirror
import scala.reflect.runtime.universe._ import scala.reflect.runtime.universe._
/** /** Scala runtime reflection
* Scala runtime reflection
* *
* @author 梦境迷离 * @author
* @since 2021/12/5 * 梦境迷离
* @version 1.0 * @since 2021/12/5
* @version 1.0
*/ */
class ScalaReflectionUtils[T: WeakTypeTag] { class ScalaReflectionUtils[T: WeakTypeTag] {
......
...@@ -24,10 +24,10 @@ package org.bitlap.tools ...@@ -24,10 +24,10 @@ package org.bitlap.tools
import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2021/6/30 * @since 2021/6/30
* @version 1.0 * @version 1.0
*/ */
class ApplyTest extends AnyFlatSpec with Matchers { class ApplyTest extends AnyFlatSpec with Matchers {
......
...@@ -24,10 +24,10 @@ package org.bitlap.tools ...@@ -24,10 +24,10 @@ package org.bitlap.tools
import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2021/6/19 * @since 2021/6/19
* @version 1.0 * @version 1.0
*/ */
class BuilderTest extends AnyFlatSpec with Matchers { class BuilderTest extends AnyFlatSpec with Matchers {
...@@ -51,7 +51,7 @@ class BuilderTest extends AnyFlatSpec with Matchers { ...@@ -51,7 +51,7 @@ class BuilderTest extends AnyFlatSpec with Matchers {
} }
"builder3" should "class with toString, non companion object" in { "builder3" should "class with toString, non companion object" in {
@toString //"toString" must be before "builder" @toString // "toString" must be before "builder"
@builder @builder
class TestClass1(val i: Int = 0, var j: Int, x: String, o: Option[String] = Some("")) class TestClass1(val i: Int = 0, var j: Int, x: String, o: Option[String] = Some(""))
val ret = TestClass1.builder().i(1).j(0).x("x").build() val ret = TestClass1.builder().i(1).j(0).x("x").build()
...@@ -89,7 +89,7 @@ class BuilderTest extends AnyFlatSpec with Matchers { ...@@ -89,7 +89,7 @@ class BuilderTest extends AnyFlatSpec with Matchers {
"builder7" should "case class with toString and companion object not in order" in { "builder7" should "case class with toString and companion object not in order" in {
@builder @builder
@toString //failed when companion object exists, fix in 0.0.6 @toString // failed when companion object exists, fix in 0.0.6
case class TestClass1(val i: Int = 0, var j: Int, x: String, o: Option[String] = Some("")) case class TestClass1(val i: Int = 0, var j: Int, x: String, o: Option[String] = Some(""))
object TestClass1 object TestClass1
val ret = TestClass1.builder().i(1).j(0).x("x").build() val ret = TestClass1.builder().i(1).j(0).x("x").build()
......
...@@ -24,10 +24,10 @@ package org.bitlap.tools ...@@ -24,10 +24,10 @@ package org.bitlap.tools
import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2021/7/3 * @since 2021/7/3
* @version 1.0 * @version 1.0
*/ */
class ConstructorTest extends AnyFlatSpec with Matchers { class ConstructorTest extends AnyFlatSpec with Matchers {
...@@ -129,8 +129,8 @@ class ConstructorTest extends AnyFlatSpec with Matchers { ...@@ -129,8 +129,8 @@ class ConstructorTest extends AnyFlatSpec with Matchers {
@builder @builder
@constructor(excludeFields = Seq("c")) @constructor(excludeFields = Seq("c"))
class A2(int: Int, val j: Int, var k: Option[String] = None, t: Option[Long] = Some(1L)) { class A2(int: Int, val j: Int, var k: Option[String] = None, t: Option[Long] = Some(1L)) {
private val a: Int = 1 private val a: Int = 1
var b: Int = 1 var b: Int = 1
protected var c: Int = _ protected var c: Int = _
def helloWorld: String = "hello world" def helloWorld: String = "hello world"
...@@ -143,19 +143,14 @@ class ConstructorTest extends AnyFlatSpec with Matchers { ...@@ -143,19 +143,14 @@ class ConstructorTest extends AnyFlatSpec with Matchers {
@toString @toString
@constructor @constructor
class TestClass12(val i: Int = 0)(var j: Int)(val k: Int) { class TestClass12(val i: Int = 0)(var j: Int)(val k: Int) {
private val a: Int = 1 private val a: Int = 1
var b: Int = 1 var b: Int = 1
protected var c: Int = _ protected var c: Int = _
} }
println(new TestClass12(1, 1, 1)(2)(3)) println(new TestClass12(1, 1, 1)(2)(3))
/** /** def <init>(i: Int, b: Int, c: Int)(j: Int)(k: Int) = { <init>(i)(j)(k); this.b = b; this.c = c }
* def <init>(i: Int, b: Int, c: Int)(j: Int)(k: Int) = {
* <init>(i)(j)(k);
* this.b = b;
* this.c = c
* }
*/ */
} }
...@@ -166,8 +161,8 @@ class ConstructorTest extends AnyFlatSpec with Matchers { ...@@ -166,8 +161,8 @@ class ConstructorTest extends AnyFlatSpec with Matchers {
@toString @toString
@constructor @constructor
class TestClass12(val i: Int = 0)(var j: Int)(val k: Int) { class TestClass12(val i: Int = 0)(var j: Int)(val k: Int) {
var b = "hello" //primitive type, support no type declared var b = "hello" // primitive type, support no type declared
var c: B = new B() //not support no type declared, `var c = new B ()` cannot be compiled. var c: B = new B() // not support no type declared, `var c = new B ()` cannot be compiled.
} }
val t = new TestClass12(1, "helloo", new B())(1)(1) val t = new TestClass12(1, "helloo", new B())(1)(1)
println(t) println(t)
......
...@@ -26,15 +26,15 @@ import org.scalatest.matchers.should.Matchers ...@@ -26,15 +26,15 @@ import org.scalatest.matchers.should.Matchers
import scala.concurrent.Future import scala.concurrent.Future
/** /** @author
* @author 梦境迷离 * 梦境迷离
* @since 2021/8/7 * @since 2021/8/7
* @version 1.0 * @version 1.0
*/ */
class ElapsedTest extends AnyFlatSpec with Matchers { class ElapsedTest extends AnyFlatSpec with Matchers {
"elapsed1" should "failed, not calculate anything, the return type is not specified" in { "elapsed1" should "failed, not calculate anything, the return type is not specified" in {
//Duration and TimeUnit must Full class name // Duration and TimeUnit must Full class name
""" """
| class A { | class A {
| @elapsed(limit = scala.concurrent.duration.Duration(1, java.util.concurrent.TimeUnit.SECONDS), logLevel = org.bitlap.tools.LogLevel.INFO) | @elapsed(limit = scala.concurrent.duration.Duration(1, java.util.concurrent.TimeUnit.SECONDS), logLevel = org.bitlap.tools.LogLevel.INFO)
...@@ -63,7 +63,7 @@ class ElapsedTest extends AnyFlatSpec with Matchers { ...@@ -63,7 +63,7 @@ class ElapsedTest extends AnyFlatSpec with Matchers {
} }
"elapsed2" should "ok, get the returnType of the method " in { "elapsed2" should "ok, get the returnType of the method " in {
//Duration and TimeUnit must Full class name // Duration and TimeUnit must Full class name
""" """
|class A { |class A {
| @elapsed(limit = scala.concurrent.duration.Duration(1, java.util.concurrent.TimeUnit.NANOSECONDS), logLevel = org.bitlap.tools.LogLevel.INFO) | @elapsed(limit = scala.concurrent.duration.Duration(1, java.util.concurrent.TimeUnit.NANOSECONDS), logLevel = org.bitlap.tools.LogLevel.INFO)
...@@ -86,7 +86,7 @@ class ElapsedTest extends AnyFlatSpec with Matchers { ...@@ -86,7 +86,7 @@ class ElapsedTest extends AnyFlatSpec with Matchers {
} }
"elapsed3" should "ok" in { "elapsed3" should "ok" in {
//Duration and TimeUnit must Full class name // Duration and TimeUnit must Full class name
""" """
| class A { | class A {
| @elapsed(limit = scala.concurrent.duration.Duration(1, java.util.concurrent.TimeUnit.NANOSECONDS), logLevel = org.bitlap.tools.LogLevel.INFO) | @elapsed(limit = scala.concurrent.duration.Duration(1, java.util.concurrent.TimeUnit.NANOSECONDS), logLevel = org.bitlap.tools.LogLevel.INFO)
...@@ -112,7 +112,7 @@ class ElapsedTest extends AnyFlatSpec with Matchers { ...@@ -112,7 +112,7 @@ class ElapsedTest extends AnyFlatSpec with Matchers {
} }
"elapsed4" should "ok, return early" in { "elapsed4" should "ok, return early" in {
//Duration and TimeUnit must Full class name // Duration and TimeUnit must Full class name
""" """
| class A { | class A {
| @elapsed(limit = scala.concurrent.duration.Duration(1, java.util.concurrent.TimeUnit.SECONDS), logLevel = org.bitlap.tools.LogLevel.INFO) | @elapsed(limit = scala.concurrent.duration.Duration(1, java.util.concurrent.TimeUnit.SECONDS), logLevel = org.bitlap.tools.LogLevel.INFO)
...@@ -210,7 +210,7 @@ class ElapsedTest extends AnyFlatSpec with Matchers { ...@@ -210,7 +210,7 @@ class ElapsedTest extends AnyFlatSpec with Matchers {
} }
} }
"elapsed7" should "ok at object but has runTime Error" in { //Why? "elapsed7" should "ok at object but has runTime Error" in { // Why?
""" """
| object A { | object A {
| private final val log1: org.slf4j.Logger = org.slf4j.LoggerFactory.getLogger(A.getClass) | private final val log1: org.slf4j.Logger = org.slf4j.LoggerFactory.getLogger(A.getClass)
...@@ -273,7 +273,7 @@ class ElapsedTest extends AnyFlatSpec with Matchers { ...@@ -273,7 +273,7 @@ class ElapsedTest extends AnyFlatSpec with Matchers {
| println("") | println("")
| "hello" | "hello"
| } | }
|""".stripMargin shouldNot compile //args not in order |""".stripMargin shouldNot compile // args not in order
} }
"elapsed10" should "multi-return" in { "elapsed10" should "multi-return" in {
class A { class A {
...@@ -344,7 +344,7 @@ class ElapsedTest extends AnyFlatSpec with Matchers { ...@@ -344,7 +344,7 @@ class ElapsedTest extends AnyFlatSpec with Matchers {
val i = 0 val i = 0
for (i <- Seq(1)) for (i <- Seq(1))
if (i == 1) { if (i == 1) {
return 1 //not support return 1 // not support
} }
0 0
} }
......
...@@ -33,8 +33,8 @@ class EmployeeTests extends AnyFlatSpec with Matchers { ...@@ -33,8 +33,8 @@ class EmployeeTests extends AnyFlatSpec with Matchers {
that match { that match {
case that: Employee => case that: Employee =>
that.canEqual(this) && that.canEqual(this) &&
this.role == that.role && this.role == that.role &&
super.equals(that) super.equals(that)
case _ => false case _ => false
} }
...@@ -57,8 +57,8 @@ class EmployeeTests extends AnyFlatSpec with Matchers { ...@@ -57,8 +57,8 @@ class EmployeeTests extends AnyFlatSpec with Matchers {
that match { that match {
case that: Person => case that: Person =>
that.canEqual(this) && that.canEqual(this) &&
this.name == that.name && this.name == that.name &&
this.age == that.age this.age == that.age
case _ => false case _ => false
} }
...@@ -73,8 +73,8 @@ class EmployeeTests extends AnyFlatSpec with Matchers { ...@@ -73,8 +73,8 @@ class EmployeeTests extends AnyFlatSpec with Matchers {
"equals1" should "ok" in { "equals1" should "ok" in {
// these first two instances should be equal // these first two instances should be equal
val nimoy = new Person("Leonard Nimoy", 82) val nimoy = new Person("Leonard Nimoy", 82)
val nimoy2 = new Person("Leonard Nimoy", 82) val nimoy2 = new Person("Leonard Nimoy", 82)
val shatner = new Person("William Shatner", 82) val shatner = new Person("William Shatner", 82)
val stewart = new Person("Patrick Stewart", 47) val stewart = new Person("Patrick Stewart", 47)
...@@ -96,9 +96,9 @@ class EmployeeTests extends AnyFlatSpec with Matchers { ...@@ -96,9 +96,9 @@ class EmployeeTests extends AnyFlatSpec with Matchers {
"equals2" should "ok" in { "equals2" should "ok" in {
// these first two instance should be equal // these first two instance should be equal
val eNimoy1 = new Employee("Leonard Nimoy", 82, "Actor") val eNimoy1 = new Employee("Leonard Nimoy", 82, "Actor")
val eNimoy2 = new Employee("Leonard Nimoy", 82, "Actor") val eNimoy2 = new Employee("Leonard Nimoy", 82, "Actor")
val pNimoy = new Person("Leonard Nimoy", 82) val pNimoy = new Person("Leonard Nimoy", 82)
val eShatner = new Employee("William Shatner", 82, "Actor") val eShatner = new Employee("William Shatner", 82, "Actor")
// equality tests // equality tests
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册