Skip to content

Commit

Permalink
Created Unique (migrated from lightdb) to generate unique strings
Browse files Browse the repository at this point in the history
  • Loading branch information
darkfrog26 committed Jan 2, 2025
1 parent a757046 commit 6b07ca7
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 11 deletions.
1 change: 1 addition & 0 deletions core/jvm/src/main/scala/rapid/VirtualThreadFiber.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class VirtualThreadFiber[Return](val task: Task[Return]) extends Blockable[Retur
try {
result = task.attempt.sync()
} catch {
case _: InterruptedException if cancelled => result = Failure(new CancellationException("Task was cancelled"))
case t: Throwable => result = Failure(t)
}
}
Expand Down
76 changes: 76 additions & 0 deletions core/shared/src/main/scala/rapid/Unique.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package rapid

import java.util.concurrent.ThreadLocalRandom

/**
* Unique String generator
*/
object Unique {
lazy val LettersLower = "abcdefghijklmnopqrstuvwxyz"
lazy val LettersUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
lazy val Numbers = "0123456789"
lazy val Readable = "ABCDEFGHJKLMNPQRSTWXYZ23456789"
lazy val Hexadecimal = s"${Numbers}abcdef"
lazy val LettersAndNumbers = s"$LettersLower$Numbers"
lazy val AllLetters = s"$LettersLower$LettersUpper"
lazy val AllLettersAndNumbers = s"$LettersLower$LettersUpper$Numbers"

/**
* Random number generator used to generate unique values. Defaults to `threadLocalRandom`.
*/
var random: Forge[Int, Int] = threadLocalRandom

/**
* The default length to use for generating unique values. Defaults to 32.
*/
var defaultLength: Int = 32

/**
* The default characters to use for generating unique values. Defaults to AllLettersAndNumbers.
*/
var defaultCharacters: String = AllLettersAndNumbers

/**
* Uses java.util.concurrent.ThreadLocalRandom to generate random numbers.
*
* @param max the maximum value to include
* @return random number between 0 and max
*/
final lazy val threadLocalRandom: Forge[Int, Int] = Forge { max =>
Task(ThreadLocalRandom.current().nextInt(max))
}

/**
* Generates a unique String using the characters supplied at the length defined.
*
* @param length the length of the resulting String. Defaults to Unique.defaultLength.
* @param characters the characters for use in the String. Defaults to Unique.defaultCharacters.
* @return a unique String
*/
def apply(length: Int = defaultLength, characters: String = defaultCharacters): Task[String] = {
val charMax = characters.length
(0 until length).map(_ => random(charMax)).tasks.map(_.map(characters.charAt).mkString)
}

/**
* Convenience functionality to generate a UUID (https://en.wikipedia.org/wiki/Universally_unique_identifier)
*
* 32 characters of unique hexadecimal values with dashes representing 36 total characters
*/
def uuid: Task[String] = Task {
val a = apply(8, Hexadecimal)
val b = apply(4, Hexadecimal)
val c = apply(3, Hexadecimal)
val d = apply(1, "89ab")
val e = apply(3, Hexadecimal)
val f = apply(12, Hexadecimal)
s"$a-$b-4$c-$d$e-$f"
}

/**
* Returns the number of possible values for a specific length and characters.
*/
def poolSize(length: Int = 32, characters: String = AllLettersAndNumbers): Task[Long] = Task {
math.pow(characters.length, length).toLong
}
}
10 changes: 0 additions & 10 deletions core/shared/src/test/scala/spec/BasicsSyncSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,5 @@ class BasicsSyncSpec extends AnyWordSpec with Matchers {
}
task.flatten.sync() should be("Success!")
}
// TODO: Re-enable once this can work with JS
/*"cancel a running task" in {
if (Platform.supportsCancel) {
val start = System.currentTimeMillis()
val fiber = Task.sleep(1.hour).map(_ => "Never").start()
fiber.cancel().sync()
a[CancellationException] should be thrownBy fiber.sync()
(System.currentTimeMillis() - start) should be < 1000L
}
}*/
}
}
12 changes: 11 additions & 1 deletion test/jvm/src/test/scala/spec/BlockableSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package spec

import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AsyncWordSpec
import rapid.{AsyncTaskSpec, Task}
import rapid.{AsyncTaskSpec, Platform, Task}

import scala.concurrent.CancellationException
import scala.concurrent.duration.DurationInt

class BlockableSpec extends AsyncWordSpec with AsyncTaskSpec with Matchers {
Expand All @@ -14,5 +15,14 @@ class BlockableSpec extends AsyncWordSpec with AsyncTaskSpec with Matchers {
c
}.map(_ should be("Finished!"))
}
"cancel a running task" in {
val start = System.currentTimeMillis()
val fiber = Task.sleep(1.hour).map(_ => "Never").start()
fiber.cancel().map { b =>
b should be(true)
a[CancellationException] should be thrownBy fiber.sync()
(System.currentTimeMillis() - start) should be < 1000L
}
}
}
}
5 changes: 5 additions & 0 deletions test/shared/src/test/scala/spec/BasicsAsyncSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,10 @@ class BasicsAsyncSpec extends AsyncWordSpec with AsyncTaskSpec with Matchers {
list should be(List("One", "Two", "Three"))
}
}
"create a Unique value" in {
Unique(length = 32).map { s =>
s.length should be(32)
}
}
}
}

0 comments on commit 6b07ca7

Please sign in to comment.