Deleting the old stuff oops.
All checks were successful
/ Build and Release Library (push) Successful in 1m32s

This commit is contained in:
Pat Garrity 2026-05-03 21:20:57 -05:00
parent 3e7de767c7
commit 4a10958731
Signed by: pfm
GPG key ID: 5CA5D21BAB7F3A76
16 changed files with 0 additions and 1061 deletions

View file

@ -1,36 +0,0 @@
package gs.std.v0.core
import java.{util => ju}
/** Implementation of [[Decoder]] for Base64 strings.
*
* Supports base64-url decoding as well.
*/
object Base64Decoder extends Decoder[B64]:
private lazy val d: ju.Base64.Decoder = ju.Base64.getDecoder()
private lazy val du: ju.Base64.Decoder = ju.Base64.getUrlDecoder()
/** @inheritDocs
*/
override def decode(input: B64): Array[Byte] =
d.decode(input.data)
/** Decode some arbitrary string data.
*
* @param input
* The data to decode.
* @return
* The decoded bytes.
*/
def decodeUnsafe(input: String): Array[Byte] =
d.decode(input)
/** Decode the base64-url encoded input.
*
* @param input
* The base64-url encoded data.
* @return
* The decoded bytes.
*/
def decodeUrl(input: B64Url): Array[Byte] =
du.decode(input.data)

View file

@ -1,43 +0,0 @@
package gs.std.v0.core
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import java.util.Base64
/** Implementation of [[Encoder]] for Base64.
*
* Supports base64-url encoding as well.
*/
object Base64Encoder extends Encoder[B64]:
private lazy val e: Base64.Encoder = Base64.getEncoder()
private lazy val eu: Base64.Encoder = Base64.getUrlEncoder()
/** @inheritDocs
*/
override def encode(input: Array[Byte]): B64 =
B64(e.encodeToString(input))
/** Encode the given bytes using base64-url.
*
* @param input
* The input data.
* @return
* The base64-url-encoded string.
*/
def encodeUrl(input: Array[Byte]): B64Url =
B64Url(eu.encodeToString(input))
/** Encode the given bytes using base64-url.
*
* @param input
* The input data.
* @param charset
* The character set of the input data.
* @return
* The base64-url-encoded string.
*/
def encodeUrl(
input: String,
charset: Charset = StandardCharsets.UTF_8
): B64Url =
encodeUrl(input.getBytes(charset))

View file

@ -1,49 +0,0 @@
package gs.std.v0.core
import java.util.Base64
/** Represents a blob -- some array of bytes.
*
* @param data
* The underlying data.
*/
final class Blob(private val data: Array[Byte]) extends IndexedSeq[Byte]:
/** @inheritDocs
*/
override def apply(i: Int): Byte = byteAt(i)
/** @inheritDocs
*/
override def length: Int = data.length
/** @return
* The number of bytes in this blob, expressed as a count.
*/
def numberOfBytes: ByteCount = ByteCount(data.length)
/** @inheritDocs
*/
override def equals(obj: Any): Boolean =
obj match
case other: Blob => data.sameElements(other.data)
case other: Array[Byte] => data.sameElements(other)
case _ => false
/** @inheritDocs
*/
override def hashCode(): Int = data.hashCode()
/** Retrieve the byte at the given index.
*
* @param index
* The index.
* @return
* The byte stored at the given index.
*/
def byteAt(index: Int): Byte = data.apply(index)
/** @return
* This byte array, encoded as a base64 string.
*/
def base64(): String = Base64.getEncoder().encodeToString(data)

View file

@ -1,138 +0,0 @@
package gs.std.v0.core
/** Opaque type for some number of bytes (>= 0).
*/
opaque type ByteCount = Long
/** Opaque type for some number of bytes (>= 0).
*/
object ByteCount:
/** 0 bytes.
*/
final val Zero: ByteCount = 0
/** 1,000 bytes.
*/
final val OneKilobyte: ByteCount = 1000
/** 1,000,000 bytes.
*/
final val OneMegabyte: ByteCount = 1000000
/** 1,000,000,000 bytes.
*/
final val OneGigabyte: ByteCount = 1000000000
/** Express the given number as a byte count. All values are normalized to the
* absolute value -- negative values are coerced to positive.
*
* @param value
* The input integer.
* @return
* The [[ByteCount]] instance.
*/
def apply(value: Long): ByteCount = Math.abs(value)
/** 1 kilobyte = 1,000 bytes
*
* @param kb
* The number of kilobytes.
* @return
* The number of bytes.
*/
def fromKilobytes(kb: Long): ByteCount =
Math.abs(kb) * 1000L
/** 1 megabyte = 1,000,000 bytes
*
* @param mb
* The number of megabytes.
* @return
* The number of bytes.
*/
def fromMegabytes(mb: Long): ByteCount =
Math.abs(mb) * 1000000L
/** 1 gigabyte = 1,000,000,000 bytes
*
* @param mb
* The number of gigabytes.
* @return
* The number of bytes.
*/
def fromGigabytes(gb: Long): ByteCount =
Math.abs(gb) * 1000000000
given CanEqual[ByteCount, ByteCount] = CanEqual.derived
given Ordering[ByteCount] with
/** @inheritDocs
*/
def compare(
x: ByteCount,
y: ByteCount
): Int =
if x > y then 1 else if x == y then 0 else -1
extension (byteCount: ByteCount)
/** @return
* The underlying `Long`.
*/
def unwrap(): Long = byteCount
/** @return
* The underlying `Long`.
*/
def toLong(): Long = byteCount
/** 1 kilobyte = 1,000 bytes
*
* @return
* The number of kilobytes represented by this count.
*/
def toKilobytes(): Double = byteCount / 1000.0
/** 1 megabyte = 1,000,000 bytes.
*
* @return
* The number of megabytes represented by this count.
*/
def toMegabytes(): Double = byteCount / 1000000.0
/** 1 gigabyte = 1,000,000,000 bytes.
*
* @return
* The number of gigabytes represented by this count.
*/
def toGigabytes(): Double = byteCount / 1000000000.0
/** Add some count to this one.
*
* @param that
* The number to add.
* @return
* The sum of the numbers.
*/
def +(that: ByteCount): ByteCount = byteCount + that
/** Multiply this count by some other count.
*
* @param that
* The number to multiply by.
* @return
* The product of the numbers.
*/
def *(that: ByteCount): ByteCount = byteCount * that
/** Check if this value is the same as some number.
*
* @param value
* The number to compare against.
* @return
* True if the values are equal, false otherwise.
*/
def equal(value: Int): Boolean = byteCount == value.toLong
end ByteCount

View file

@ -1,37 +0,0 @@
package gs.std.v0.core
import java.time.Instant
/** Opaque type that represents the instant something was created.
*/
opaque type CreatedAt = Instant
/** Opaque type that represents the instant something was created.
*/
object CreatedAt:
/** Instantiate a new [[CreatedAt]] from the given `java.time.Instant`.
*
* @param value
* The value to semantically represent.
* @return
* The new [[CreatedAt]].
*/
def apply(value: Instant): CreatedAt = value
given CanEqual[CreatedAt, CreatedAt] = CanEqual.derived
given Ordering[CreatedAt] = Ordering[Instant]
extension (createdAt: Instant)
/** @return
* The underlying `java.time.Instant`.
*/
def unwrap(): Instant = createdAt
/** @return
* The underlying `java.time.Instant`.
*/
def toInstant(): Instant = createdAt
end CreatedAt

View file

@ -1,13 +0,0 @@
package gs.std.v0.core
/** Interface for byte decoding from encoded String formats.
*/
trait Decoder[-A <: EncodedString]:
/** Decode an input string to an array of bytes.
*
* @param input
* The input to decode.
* @return
* The decoded byte array.
*/
def decode(input: A): Array[Byte]

View file

@ -1,58 +0,0 @@
package gs.std.v0.core
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
/** Helper functions for encoding data.
*/
object Encode:
/** Encode an array of bytes using base64.
*
* @param input
* The bytes to encode.
* @return
* The base64 string.
*/
def base64(input: Array[Byte]): B64 = Base64Encoder.encode(input)
/** Encode a string using base64.
*
* @param input
* The string to encode.
* @param charset
* The character set of the input string.
* @return
* The base64 string.
*/
def base64(
input: String,
charset: Charset = StandardCharsets.UTF_8
): B64 =
Base64Encoder.encode(input, charset)
/** Encode an array of bytes using hexadecimal.
*
* @param input
* The bytes to encode.
* @return
* The hexadecimal string.
*/
def hex(input: Array[Byte]): Hex = HexEncoder.encode(input)
/** Encode a string using hexadecimal.
*
* @param input
* The string to encode.
* @param charset
* The character set of the input string.
* @return
* The hexadecimal string.
*/
def hex(
input: String,
charset: Charset = StandardCharsets.UTF_8
): Hex =
HexEncoder.encode(input, charset)
end Encode

View file

@ -1,162 +0,0 @@
package gs.std.v0.core
/** Represents string-encoded data.
*
* See:
* - [[B64]]
* - [[Hex]]
*/
trait EncodedString:
/** @return
* The encoded data (expressed as a string).
*/
def data: String
/** @return
* Decode the data to a byte array.
*/
def decode(): Array[Byte]
/** Represents Base64-encoded data.
*
* @param data
* The encoded data.
*/
/** Represents Base64-encoded data.
*
* @param data
* The encoded data.
*/
final class B64(
val data: String
) extends EncodedString:
/** @inheritDocs
*/
def decode(): Array[Byte] = Base64Decoder.decode(this)
/** @inheritDocs
*/
override def equals(obj: Any): Boolean =
obj match
case other: B64 => data == other.data
/** @inheritDocs
*/
override def hashCode(): Int = data.hashCode()
/** @inheritDocs
*/
override def toString(): String = data
object B64:
/** Instantiate [[B64]] from the given string. Assumes that the input is
* base64-encoded.
*
* This function does NOT validate the input.
*
* @param data
* The encoded data.
* @return
* The new [[B64]] instance.
*/
def apply(
data: String
): B64 = new B64(data)
given CanEqual[B64, B64] = CanEqual.derived
end B64
/** Represents Base64-url-encoded data.
*
* @param data
* The encoded data.
*/
final class B64Url(
val data: String
) extends EncodedString:
/** @inheritDocs
*/
def decode(): Array[Byte] = Base64Decoder.decodeUrl(this)
/** @inheritDocs
*/
override def equals(obj: Any): Boolean =
obj match
case other: B64Url => data == other.data
/** @inheritDocs
*/
override def hashCode(): Int = data.hashCode()
/** @inheritDocs
*/
override def toString(): String = data
object B64Url:
/** Instantiate [[B64Url]] from the given string. Assumes that the input is
* base64-encoded.
*
* This function does NOT validate the input.
*
* @param data
* The encoded data.
* @return
* The new [[B64Url]] instance.
*/
def apply(
data: String
): B64Url = new B64Url(data)
given CanEqual[B64Url, B64Url] = CanEqual.derived
end B64Url
/** Represents Hex-encoded data.
*
* @param data
* The encoded data.
*/
final class Hex(
val data: String
) extends EncodedString:
/** @inheritDocs
*/
def decode(): Array[Byte] = HexDecoder.decode(this)
/** @inheritDocs
*/
override def equals(obj: Any): Boolean =
obj match
case other: Hex => data == other.data
/** @inheritDocs
*/
override def hashCode(): Int = data.hashCode()
/** @inheritDocs
*/
override def toString(): String = data
object Hex:
/** Instantiate [[Hex]] from the given string. Assumes that the input is
* hex-encoded.
*
* This function does NOT validate the input.
*
* @param data
* The encoded data.
* @return
* The new [[Hex]] instance.
*/
def apply(
data: String
): Hex = new Hex(data)
given CanEqual[Hex, Hex] = CanEqual.derived
end Hex

View file

@ -1,44 +0,0 @@
package gs.std.v0.core
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
/** Interface for byte encoding to String formats.
*/
trait Encoder[+A <: EncodedString]:
/** Encode an array of bytes as a string.
*
* @param input
* The bytes to encode.
* @return
* The encoded string.
*/
def encode(input: Array[Byte]): A
/** Encode a string as a string.
*
* @param input
* The string to encode.
* @param charset
* The character set of the input string.
* @return
* The encoded string.
*/
def encode(
input: String,
charset: Charset = StandardCharsets.UTF_8
): A = encode(input.getBytes(charset))
object Encoder:
/** @return
* The [[Base64Encoder]], typed to `Encoder[Encoded]`.
*/
def base64(): Encoder[EncodedString] = Base64Encoder
/** @return
* The [[HexEncoder]], typed to `Encoder[Encoded]`.
*/
def hex(): Encoder[EncodedString] = HexEncoder
end Encoder

View file

@ -1,23 +0,0 @@
package gs.std.v0.core
import java.util.HexFormat
/** Implementation of [[Decoder]] for Hexadecimal strings.
*/
object HexDecoder extends Decoder[Hex]:
private lazy val h: HexFormat = HexFormat.of()
/** @inheritDocs
*/
override def decode(input: Hex): Array[Byte] =
h.parseHex(input.data)
/** Decode some arbitrary string data.
*
* @param input
* The data to decode.
* @return
* The decoded bytes.
*/
def decodeUnsafe(input: String): Array[Byte] =
h.parseHex(input)

View file

@ -1,13 +0,0 @@
package gs.std.v0.core
import java.util.HexFormat
/** Implementation of [[Encoder]] for Hexadecimal strings.
*/
object HexEncoder extends Encoder[Hex]:
private lazy val h: HexFormat = HexFormat.of()
/** @inheritDocs
*/
override def encode(input: Array[Byte]): Hex =
Hex(h.formatHex(input))

View file

@ -1,108 +0,0 @@
package gs.std.v0.core
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
/** Opaque type representing a MD5 hash.
*/
opaque type MD5 = Array[Byte]
/** Opaque type representing a MD5 hash.
*/
object MD5:
/** MD5 hashes are exactly 16 bytes.
*/
final val NumberOfBytes: ByteCount = ByteCount(16)
/** The algorithm name is "MD5".
*/
final val Algorithm: String = "MD5"
/** Instantiate a [[MD5]] from the given byte array. This function does not
* know whether the array is actually a calculated hash.
*
* Typically used for loading pre-validated hashes (e.g. from a database).
*
* @param bytes
* The bytes - must contain exactly 16 bytes.
* @return
* The new [[MD5]] instance.
*/
def fromBytes(bytes: Array[Byte]): MD5 =
if NumberOfBytes.equal(bytes.length) then bytes
else
throw IllegalArgumentException(
s"MD5 values must be exactly $NumberOfBytes bytes."
)
/** Calculate the MD5 hash for the given byte array.
*
* @param data
* The byte array.
* @return
* The calculated [[MD5]].
*/
def calculate(data: Array[Byte]): MD5 =
MessageDigest.getInstance(Algorithm).digest(data)
/* Calculate the MD5 hash for the given string.
*
* @param data The string data.
* @param charset The character set of the string. Defaults to UTF-8.
* @return The calculated [[MD5]]. */
def calculate(
data: String,
charset: Charset = StandardCharsets.UTF_8
): MD5 =
calculate(data.getBytes(charset))
extension (md5: MD5)
/** @return
* The underlying byte array.
*/
def unwrap(): Array[Byte] = md5
/** @return
* The underlying byte array.
*/
def toBytes(): Array[Byte] = md5
/** Get the byte at the given index (0 to 15).
*
* Throws an exception if an out-of-bound index is given.
*
* @param index
* The byte index (0 to 15).
* @return
* The byte at the specified index.
*/
def getByte(index: Int): Byte =
if index < 0 || index >= NumberOfBytes.unwrap() then
throw IndexOutOfBoundsException(
s"Index $index out of MD5 bound of $NumberOfBytes bytes."
)
else md5.apply(index)
/** Determine if this hash is the same as some other hash. Compares each
* byte in order.
*
* @param other
* The [[MD5]] to compare against.
* @return
* True if the hashes are identical, false otherwise.
*/
def isSame(other: MD5): Boolean = md5.sameElements(other)
/** @return
* This hash encoded to a Base64 string.
*/
def base64(): EncodedString = Base64Encoder.encode(md5)
/** @return
* This hash encoded to a Hexadecimal string.
*/
def hex(): EncodedString = HexEncoder.encode(md5)
end MD5

View file

@ -1,84 +0,0 @@
package gs.std.v0.core
/** Opaque type for the natural numbers (including 0).
*/
opaque type Nat = Int
/** Opaque type for the natural numbers (including 0).
*/
object Nat:
sealed trait Invalid
object Invalid extends Invalid
/** The number 0.
*/
final val Zero: Nat = 0
/** The number 1.
*/
final val One: Nat = 1
/** Express the given integer as a natural number.
*
* Throws an `IllegalArgumentException` if a negative value is given as
* input.
*
* @param value
* The input integer.
* @return
* The [[Nat]] instance.
*/
def apply(value: Int): Nat =
if value >= 0 then value
else throw new IllegalArgumentException("Nat values must be 0 or greater.")
def validate(value: Int): Either[Invalid, Nat] =
if value >= 0 then Right(value) else Left(Invalid)
given CanEqual[Nat, Nat] = CanEqual.derived
given Ordering[Nat] with
/** @inheritDocs
*/
def compare(
x: Nat,
y: Nat
): Int = x - y
extension (nat: Nat)
/** @return
* The underlying integer.
*/
def unwrap(): Int = nat
/** @return
* The next integer.
*/
def next(): Nat = nat + 1
/** @return
* The next integer.
*/
def increment(): Nat = nat + 1
/** Add some natural number to this one.
*
* @param that
* The number to add.
* @return
* The sum of the numbers.
*/
def +(that: Nat): Nat = nat + that
/** Multiply this natural number by some other natural number.
*
* @param that
* The number to multiply by.
* @return
* The product of the numbers.
*/
def *(that: Nat): Nat = nat * that
end Nat

View file

@ -1,114 +0,0 @@
package gs.std.v0.core
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
/** Opaque type representing a SHA-256 hash.
*
* The actual underlying type is an array of bytes.
*/
opaque type SHA256 = Array[Byte]
/** Opaque type representing a SHA-256 hash.
*/
object SHA256:
/** SHA-256 hashes are exactly 32 bytes.
*/
final val NumberOfBytes: ByteCount = ByteCount(32)
/** The algorithm name is "SHA-256".
*/
final val Algorithm: String = "SHA-256"
/** Instantiate a [[SHA256]] from the given byte array. This function does not
* know whether the array is actually a calculated hash.
*
* Typically used for loading pre-validated hashes (e.g. from a database).
*
* @param bytes
* The bytes - must contain exactly 32 bytes.
* @return
* The new [[SHA256]] instance.
*/
def fromBytes(bytes: Array[Byte]): SHA256 =
if NumberOfBytes.equal(bytes.length) then bytes
else
throw IllegalArgumentException(
s"SHA-256 values must be exactly $NumberOfBytes bytes."
)
/** Calculate the SHA-256 hash for the given byte array.
*
* @param data
* The byte array.
* @return
* The calculated [[SHA256]].
*/
def calculate(data: Array[Byte]): SHA256 =
MessageDigest.getInstance(Algorithm).digest(data)
/** Calculate the SHA-256 hash for the given string.
*
* @param data
* The string data.
* @param charset
* The character set of the string. Defaults to UTF-8.
* @return
* The calculated [[SHA256]].
*/
def calculate(
data: String,
charset: Charset = StandardCharsets.UTF_8
): SHA256 =
calculate(data.getBytes(charset))
extension (sha: SHA256)
/** @return
* The underlying byte array.
*/
def unwrap(): Array[Byte] = sha
/** @return
* The underlying byte array.
*/
def toBytes(): Array[Byte] = sha
/** Get the byte at the given index (0 to 31).
*
* Throws an exception if an out-of-bound index is given.
*
* @param index
* The byte index (0 to 31).
* @return
* The byte at the specified index.
*/
def getByte(index: Int): Byte =
if index < 0 || index >= NumberOfBytes.unwrap() then
throw IndexOutOfBoundsException(
s"Index $index out of SHA-256 bound of $NumberOfBytes bytes."
)
else sha.apply(index)
/** Determine if this hash is the same as some other hash. Compares each
* byte in order.
*
* @param other
* The [[SHA256]] to compare against.
* @return
* True if the hashes are identical, false otherwise.
*/
def isSame(other: SHA256): Boolean = sha.sameElements(other)
/** @return
* This hash encoded to a Base64 string.
*/
def base64(): EncodedString = Base64Encoder.encode(sha)
/** @return
* This hash encoded to a Hexadecimal string.
*/
def hex(): EncodedString = HexEncoder.encode(sha)
end SHA256

View file

@ -1,102 +0,0 @@
package gs.std.v0.core
/** Opaque type for collection sizes. Values are guaranteed to be 0 or greater.
*/
opaque type Size = Int
/** Opaque type for collection sizes. Values are guaranteed to be 0 or greater.
*/
object Size:
sealed trait Invalid
object Invalid extends Invalid
/** The size 0.
*/
final val Zero: Size = 0
/** The size 1.
*/
final val One: Size = 1
/** Express the given integer as a size.
*
* Throws an `IllegalArgumentException` if a negative value is given as
* input.
*
* @param value
* The input integer.
* @return
* The [[Size]] instance.
*/
def apply(value: Int): Size =
if value >= 0 then value
else throw new IllegalArgumentException("Size values must be 0 or greater.")
def validate(value: Int): Either[Invalid, Size] =
if value >= 0 then Right(value) else Left(Invalid)
/** Express the size of any collection.
*
* @param iter
* The collection.
* @return
* The size of the collection.
*/
def of(iter: Iterable[?]): Size = iter.size
/** Express the size of any array.
*
* @param arr
* The array.
* @return
* The size of the array.
*/
def of(arr: Array[?]): Size = arr.length
given CanEqual[Size, Size] = CanEqual.derived
given Ordering[Size] with
/** @inheritDocs
*/
def compare(
x: Size,
y: Size
): Int = x - y
extension (size: Size)
/** @return
* The underlying integer.
*/
def unwrap(): Int = size
/** @return
* The next value.
*/
def next(): Size = size + 1
/** @return
* The next value.
*/
def increment(): Size = size + 1
/** Add some size to this one.
*
* @param that
* The number to add.
* @return
* The sum of the numbers.
*/
def +(that: Size): Size = size + that
/** Multiply this size by some other size.
*
* @param that
* The number to multiply by.
* @return
* The product of the numbers.
*/
def *(that: Size): Size = size * that
end Size

View file

@ -1,37 +0,0 @@
package gs.std.v0.core
import java.time.Instant
/** Opaque type that represents the instant something was updated.
*/
opaque type UpdatedAt = Instant
/** Opaque type that represents the instant something was updated.
*/
object UpdatedAt:
/** Instantiate a new [[UpdatedAt]] from the given `java.time.Instant`.
*
* @param value
* The value to semantically represent.
* @return
* The new [[UpdatedAt]].
*/
def apply(value: Instant): UpdatedAt = value
given CanEqual[UpdatedAt, UpdatedAt] = CanEqual.derived
given Ordering[UpdatedAt] = Ordering[Instant]
extension (updatedAt: Instant)
/** @return
* The underlying `java.time.Instant`.
*/
def unwrap(): Instant = updatedAt
/** @return
* The underlying `java.time.Instant`.
*/
def toInstant(): Instant = updatedAt
end UpdatedAt