From e357e48f44434820dce192b7175082daadaedf9f Mon Sep 17 00:00:00 2001 From: "sergei.winitzki" Date: Thu, 27 Jul 2023 16:35:10 +0200 Subject: [PATCH 1/3] implement SourceUUIDs --- sourcecode/src-2/sourcecode/Macros.scala | 10 ++++++++++ sourcecode/src-3/sourcecode/Macros.scala | 11 +++++++++++ sourcecode/src/sourcecode/SourceContext.scala | 7 +++++++ .../{EnumExaple.scala => EnumExample.scala} | 2 +- .../test/src/sourcecode/SourceUUID.scala | 19 +++++++++++++++++++ sourcecode/test/src/sourcecode/Tests.scala | 4 +++- 6 files changed, 51 insertions(+), 2 deletions(-) rename sourcecode/test/src/sourcecode/{EnumExaple.scala => EnumExample.scala} (95%) create mode 100644 sourcecode/test/src/sourcecode/SourceUUID.scala diff --git a/sourcecode/src-2/sourcecode/Macros.scala b/sourcecode/src-2/sourcecode/Macros.scala index 98e4a80..2b45ef5 100644 --- a/sourcecode/src-2/sourcecode/Macros.scala +++ b/sourcecode/src-2/sourcecode/Macros.scala @@ -51,6 +51,10 @@ trait ArgsMacros { implicit def generate: Args = macro Macros.argsImpl } +trait UUIDMacros { + implicit def generate: SourceUUID = macro Macros.uuidImpl +} + object Util{ def isSynthetic(c: Compat.Context)(s: c.Symbol) = isSyntheticName(getName(c)(s)) def isSyntheticName(name: String) = { @@ -195,4 +199,10 @@ object Macros { }.mkString.dropRight(1) c.Expr[T](q"""${c.prefix}($renderedPath)""") } + + def uuidImpl(c: Compat.Context): c.Expr[SourceUUID] = { + import c.universe._ + val uuid = java.util.UUID.randomUUID() + c.Expr[SourceUUID](q"""${c.prefix}($uuid)""") + } } diff --git a/sourcecode/src-3/sourcecode/Macros.scala b/sourcecode/src-3/sourcecode/Macros.scala index 69488c4..d722a5e 100644 --- a/sourcecode/src-3/sourcecode/Macros.scala +++ b/sourcecode/src-3/sourcecode/Macros.scala @@ -63,6 +63,11 @@ trait ArgsMacros { ${ Macros.argsImpl } } +trait UUIDMacros { + inline implicit def generate: SourceUUID = + ${ Macros.uuidImpl } +} + object Util{ def isSynthetic(using Quotes)(s: quotes.reflect.Symbol) = isSyntheticName(getName(s)) def isSyntheticName(name: String) = { @@ -167,6 +172,12 @@ object Macros { '{Enclosing(${Expr(path)})} } + def uuidImpl(using Quotes): Expr[SourceUUID] = { + import quotes.reflect._ + val uuid = java.util.UUID.randomUUID() + '{ sourcecode.SourceUUID(${ Expr(uuid) }) } + } + def enclosingMachineImpl(using Quotes): Expr[Enclosing.Machine] = { val path = enclosing(machine = true)(_ => true) '{Enclosing.Machine(${Expr(path)})} diff --git a/sourcecode/src/sourcecode/SourceContext.scala b/sourcecode/src/sourcecode/SourceContext.scala index c11a2e5..724852b 100644 --- a/sourcecode/src/sourcecode/SourceContext.scala +++ b/sourcecode/src/sourcecode/SourceContext.scala @@ -43,3 +43,10 @@ object Text extends TextMacros case class Args(value: Seq[Seq[Text[_]]]) extends SourceValue[Seq[Seq[Text[_]]]] object Args extends SourceCompanion[Seq[Seq[Text[_]]], Args](new Args(_)) with ArgsMacros + +// Unique identifiers, labels, and other IDs generated at compile time. +import java.util.UUID + +final case class SourceUUID(value: UUID) extends SourceValue[UUID] + +object SourceUUID extends SourceCompanion[UUID, SourceUUID](new SourceUUID(_)) with UUIDMacros diff --git a/sourcecode/test/src/sourcecode/EnumExaple.scala b/sourcecode/test/src/sourcecode/EnumExample.scala similarity index 95% rename from sourcecode/test/src/sourcecode/EnumExaple.scala rename to sourcecode/test/src/sourcecode/EnumExample.scala index 922ec19..5b3aede 100644 --- a/sourcecode/test/src/sourcecode/EnumExaple.scala +++ b/sourcecode/test/src/sourcecode/EnumExample.scala @@ -1,6 +1,6 @@ package sourcecode -object EnumExaple { +object EnumExample { def run() = { case class EnumValue(name: String){ override def toString = name diff --git a/sourcecode/test/src/sourcecode/SourceUUID.scala b/sourcecode/test/src/sourcecode/SourceUUID.scala new file mode 100644 index 0000000..048eb54 --- /dev/null +++ b/sourcecode/test/src/sourcecode/SourceUUID.scala @@ -0,0 +1,19 @@ +package sourcecode + +object SourceUUID { + def run() = { + // Verify that UUIDs are different and are generated at compile time, not at run time. + def generateUUIDs() = { + val uuid1 = implicitly[sourcecode.SourceUUID] + val uuid2 = implicitly[sourcecode.SourceUUID] + (uuid1, uuid2) + } + + val (u1a, u2a) = generateUUIDs() + val (u1b, u2b) = generateUUIDs() + + assert(u1a != u2a) // The two UUIDs are different. + assert(u1a == u1b) // The UUIDs are generated at compile time. + assert(u2a == u2b) // So, calling `generateUUIDs()` several times will produce the same results. + } +} diff --git a/sourcecode/test/src/sourcecode/Tests.scala b/sourcecode/test/src/sourcecode/Tests.scala index 4bfd38b..054291a 100644 --- a/sourcecode/test/src/sourcecode/Tests.scala +++ b/sourcecode/test/src/sourcecode/Tests.scala @@ -14,7 +14,7 @@ object Tests{ println("================Test Begin================") Apply.applyRun() Implicits.implicitRun() - EnumExaple.run() + EnumExample.run() EnumFull.run() NoSynthetic.run() Synthetic.run() @@ -32,6 +32,8 @@ object Tests{ DebugLite.main() println("================Regressions===============") Regressions.main() + println("================Unique IDs================") + SourceUUID.run() println("================Test Ended================") } } From 6b1f7e8033357e2dea0fc638e31f6c28c6ba4b4a Mon Sep 17 00:00:00 2001 From: "sergei.winitzki" Date: Thu, 27 Jul 2023 19:00:43 +0200 Subject: [PATCH 2/3] better test --- sourcecode/test/src/sourcecode/SourceUUID.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sourcecode/test/src/sourcecode/SourceUUID.scala b/sourcecode/test/src/sourcecode/SourceUUID.scala index 048eb54..4e21ba2 100644 --- a/sourcecode/test/src/sourcecode/SourceUUID.scala +++ b/sourcecode/test/src/sourcecode/SourceUUID.scala @@ -4,16 +4,16 @@ object SourceUUID { def run() = { // Verify that UUIDs are different and are generated at compile time, not at run time. def generateUUIDs() = { - val uuid1 = implicitly[sourcecode.SourceUUID] - val uuid2 = implicitly[sourcecode.SourceUUID] + val uuid1 = implicitly[sourcecode.SourceUUID] // Generate some UUID. + val uuid2 = implicitly[sourcecode.SourceUUID] // This will be a different UUID. (uuid1, uuid2) } - val (u1a, u2a) = generateUUIDs() - val (u1b, u2b) = generateUUIDs() + val (u1a, u2a) = generateUUIDs() // Generate a pair of UUIDs. + val (u1b, u2b) = generateUUIDs() // This will be the same pair of UUID. - assert(u1a != u2a) // The two UUIDs are different. - assert(u1a == u1b) // The UUIDs are generated at compile time. - assert(u2a == u2b) // So, calling `generateUUIDs()` several times will produce the same results. + assert(u1a != u2a && u1a.toString != u2a.toString) // Verify that the two UUIDs are different. + assert(u1a == u1b && u1a.toString == u1b.toString) // The UUIDs are generated at compile time. + assert(u2a == u2b && u2a.toString == u2b.toString) // So, calling `generateUUIDs()` several times will produce the same results. } } From 56b698e8bcf5a60f7702e0984976335d0111a8fe Mon Sep 17 00:00:00 2001 From: "sergei.winitzki" Date: Fri, 28 Jul 2023 11:44:58 +0200 Subject: [PATCH 3/3] better tests --- sourcecode/src-2/sourcecode/Macros.scala | 4 +- sourcecode/src-3/sourcecode/Macros.scala | 8 +++- sourcecode/src/sourcecode/SourceContext.scala | 2 +- .../test/src/sourcecode/SourceUUID.scala | 19 -------- .../test/src/sourcecode/SourceUUIDTests.scala | 47 +++++++++++++++++++ sourcecode/test/src/sourcecode/Tests.scala | 4 +- 6 files changed, 59 insertions(+), 25 deletions(-) delete mode 100644 sourcecode/test/src/sourcecode/SourceUUID.scala create mode 100644 sourcecode/test/src/sourcecode/SourceUUIDTests.scala diff --git a/sourcecode/src-2/sourcecode/Macros.scala b/sourcecode/src-2/sourcecode/Macros.scala index 2b45ef5..a2a8ae4 100644 --- a/sourcecode/src-2/sourcecode/Macros.scala +++ b/sourcecode/src-2/sourcecode/Macros.scala @@ -199,9 +199,11 @@ object Macros { }.mkString.dropRight(1) c.Expr[T](q"""${c.prefix}($renderedPath)""") } - + def uuidImpl(c: Compat.Context): c.Expr[SourceUUID] = { import c.universe._ + import java.util.UUID + implicit val l: Liftable[UUID] = Liftable((in: UUID) => q"_root_.java.util.UUID.fromString(${in.toString})") val uuid = java.util.UUID.randomUUID() c.Expr[SourceUUID](q"""${c.prefix}($uuid)""") } diff --git a/sourcecode/src-3/sourcecode/Macros.scala b/sourcecode/src-3/sourcecode/Macros.scala index d722a5e..4448c33 100644 --- a/sourcecode/src-3/sourcecode/Macros.scala +++ b/sourcecode/src-3/sourcecode/Macros.scala @@ -174,10 +174,14 @@ object Macros { def uuidImpl(using Quotes): Expr[SourceUUID] = { import quotes.reflect._ - val uuid = java.util.UUID.randomUUID() + import java.util.UUID + given ToExpr[UUID] with { + def apply(x: UUID)(using Quotes) = '{ _root_.java.util.UUID.fromString(${ Expr(x.toString) }.toString) } + } + val uuid = UUID.randomUUID() '{ sourcecode.SourceUUID(${ Expr(uuid) }) } } - + def enclosingMachineImpl(using Quotes): Expr[Enclosing.Machine] = { val path = enclosing(machine = true)(_ => true) '{Enclosing.Machine(${Expr(path)})} diff --git a/sourcecode/src/sourcecode/SourceContext.scala b/sourcecode/src/sourcecode/SourceContext.scala index 724852b..13d77c2 100644 --- a/sourcecode/src/sourcecode/SourceContext.scala +++ b/sourcecode/src/sourcecode/SourceContext.scala @@ -47,6 +47,6 @@ object Args extends SourceCompanion[Seq[Seq[Text[_]]], Args](new Args(_)) with A // Unique identifiers, labels, and other IDs generated at compile time. import java.util.UUID -final case class SourceUUID(value: UUID) extends SourceValue[UUID] +case class SourceUUID(value: UUID) extends SourceValue[UUID] object SourceUUID extends SourceCompanion[UUID, SourceUUID](new SourceUUID(_)) with UUIDMacros diff --git a/sourcecode/test/src/sourcecode/SourceUUID.scala b/sourcecode/test/src/sourcecode/SourceUUID.scala deleted file mode 100644 index 4e21ba2..0000000 --- a/sourcecode/test/src/sourcecode/SourceUUID.scala +++ /dev/null @@ -1,19 +0,0 @@ -package sourcecode - -object SourceUUID { - def run() = { - // Verify that UUIDs are different and are generated at compile time, not at run time. - def generateUUIDs() = { - val uuid1 = implicitly[sourcecode.SourceUUID] // Generate some UUID. - val uuid2 = implicitly[sourcecode.SourceUUID] // This will be a different UUID. - (uuid1, uuid2) - } - - val (u1a, u2a) = generateUUIDs() // Generate a pair of UUIDs. - val (u1b, u2b) = generateUUIDs() // This will be the same pair of UUID. - - assert(u1a != u2a && u1a.toString != u2a.toString) // Verify that the two UUIDs are different. - assert(u1a == u1b && u1a.toString == u1b.toString) // The UUIDs are generated at compile time. - assert(u2a == u2b && u2a.toString == u2b.toString) // So, calling `generateUUIDs()` several times will produce the same results. - } -} diff --git a/sourcecode/test/src/sourcecode/SourceUUIDTests.scala b/sourcecode/test/src/sourcecode/SourceUUIDTests.scala new file mode 100644 index 0000000..6136ecb --- /dev/null +++ b/sourcecode/test/src/sourcecode/SourceUUIDTests.scala @@ -0,0 +1,47 @@ +package sourcecode + +import java.util.UUID + +object SourceUUIDTests { + case class DataWithId(x: Int, id: UUID) + + case class DataWithImplicitId(x: Int)(implicit val id: sourcecode.SourceUUID) + + def run() = { + // Verify that UUIDs are different and are generated at compile time, not at run time. + def generateUUIDs() = { + val uuid1 = implicitly[sourcecode.SourceUUID].value + val uuid2 = implicitly[sourcecode.SourceUUID].value + (uuid1, uuid2) + } + + val (u1a, u2a) = generateUUIDs() // Generate a pair of UUIDs. + val (u1b, u2b) = generateUUIDs() // This will have the same UUIDs. + assert(u1a != u2a && u1a.toString != u2a.toString, "the two UUIDs are different") + assert(u1a == u1b && u1a.toString == u1b.toString, "the UUIDs are generated at compile time") + assert(u2a == u2b && u2a.toString == u2b.toString, "calling `generateUUIDs()` several times will produce the same results") + + def generateData1(x: Int)(implicit uuid: sourcecode.SourceUUID) = DataWithId(x, uuid.value) + + def generateData2(x: Int) = DataWithId(x, implicitly[sourcecode.SourceUUID].value) + + val u10 = generateData1(10) + val u20 = generateData2(20) + val u30 = generateData1(30) + val u40 = generateData2(40) + + assert(u10.id.toString != u30.id.toString, "generateData1() produces different IDs each time") + assert(u20.id.toString == u40.id.toString, "generateData2() produces the same IDs each time") + assert(u10.id.toString != u20.id.toString && u30.id.toString != u40.id.toString, "generateData1() and generateData2() produce different IDs") + + val u50 = DataWithImplicitId(50) + val u60 = DataWithImplicitId(60) + assert(u50.id.toString != u60.id.toString, "DataWithImplicitId() produces different IDs each time") + + def generateData3(x: Int) = DataWithImplicitId(x) + + val u70 = generateData3(70) + val u80 = generateData3(80) + assert(u70.id.toString == u80.id.toString, "generateData3() produces the same IDs each time") + } +} diff --git a/sourcecode/test/src/sourcecode/Tests.scala b/sourcecode/test/src/sourcecode/Tests.scala index 054291a..7a86804 100644 --- a/sourcecode/test/src/sourcecode/Tests.scala +++ b/sourcecode/test/src/sourcecode/Tests.scala @@ -30,10 +30,10 @@ object Tests{ DebugName.main() println("================Debug Lite================") DebugLite.main() + println("================Unique IDs================") + SourceUUIDTests.run() println("================Regressions===============") Regressions.main() - println("================Unique IDs================") - SourceUUID.run() println("================Test Ended================") } }