Macaroni
Macaroni provides a collection of reusable utilities and helpers that could be useful in any project. It includes modules for meta-programming, parsing, resource pooling, cryptography, and filesystem operations.
Meta Programming
The meta
package provides type-level programming utilities:
Type Helpers
The Types
object provides type-level helpers for working with tuples and type intersections/unions:
// Intersection type with Any default
type IAnyType[T <: Tuple] = Tuple.Fold[T, Any, [x, y] =>> x & y]
// Intersection type with Nothing default
type INothingType[T <: Tuple] = Tuple.Fold[T, Nothing, [x, y] =>> x & y]
// Union type with Any default
type UAnyType[T <: Tuple] = Tuple.Fold[T, Any, [x, y] =>> x | y]
These are particularly useful when working with case class fields via mirrors to create intersection or union types of all fields.
Parser Combinators
The parsers
package provides a parser combinator library, which powers the Friday JSON parser. The implementation is based on the approach described in Functional Programming in Scala (2nd Ed).
Key components:
Parsers
trait - Defines the core parsing primitives and combinatorsReference
implementation - A concrete implementation of the parser combinatorsLocation
- Tracks position in input for error reportingParseError
- Provides detailed error messages with line/column information
Example usage:
import dev.wishingtree.branch.macaroni.parsers.{Parsers, Reference}
// Create a simple parser
val parser = Reference.string("hello")
// Run the parser
parser.run("hello") // Right("hello")
parser.run("world") // Left(ParseError)
Resource Pool
The ResourcePool[R]
trait provides a simple way to manage pools of reusable resources. Key features:
- Semaphore-based access control (default 5 concurrent resources)
- Lazy resource initialization (resources created on first use)
- Optional health checking
- Automatic resource cleanup
To implement a resource pool:
import dev.wishingtree.branch.macaroni.pool.ResourcePool
class DatabasePool extends ResourcePool[Connection] {
def acquire: Connection = // Create new connection
def release(conn: Connection): Unit = conn.close()
// Optional: Override to add connection testing
override def test(conn: Connection): Boolean =
conn.isValid(5000)
}
Filesystem Operations
The fs
package provides convenient filesystem utilities through the PathOps
object:
import dev.wishingtree.branch.macaroni.fs.PathOps.*
// Get working directory
val wd: Path = PathOps.wd
// Path operations using extension methods
val configPath = wd / "config" / "app.conf" // Append segments using /
val otherPath = Path.of("other/path")
val combined = configPath / otherPath // Can combine with other Paths
// Path relativization
val relative = configPath.relativeTo(wd) // Get path relative to working dir
val relativeStr = configPath.relativeTo("/home/user") // Or relative to string path
// Convert path to sequence of segments
val segments: Seq[String] = configPath.toSeq // Split path into parts
// Pattern matching on paths using / extractor
configPath match {
case parent / "app.conf" => println(s"Found config in $parent")
case _ => println("Config not found")
}
// String interpolation
val srcPath = p"src/main/resources" // Create Path using p"" interpolator
val pattern = ci"Config.JSON" // Case-insensitive pattern matching
// Empty path constant
val emptyPath = >> // Represents Path.of("")
The PathOps utilities make it easy to work with filesystem paths in a more idiomatic Scala way, with extension methods for common operations and pattern matching support.
Cryptography
The crypto
package provides common cryptographic operations:
- Secure random key generation
- Base64 encoding/decoding
- PBKDF2 password hashing
- HMAC message authentication
- AES encryption/decryption
Example usage:
import dev.wishingtree.branch.macaroni.crypto.Crypto
// Generate random keys
val publicKey = Crypto.generatePublicKey(16) // 16-char key using limited ASCII range
val privateKey = Crypto.generatePrivateKey(32) // 32-char key using full ASCII range
// Password hashing with PBKDF2
val hash = Crypto.pbkdf2Hash("mypassword123")
val isValid = Crypto.validatePbkdf2Hash("mypassword123", hash) // true
// AES encryption/decryption
val message = "sensitive data"
val encrypted = Crypto.aesEncrypt(message, privateKey).get
val decrypted = Crypto.aesDecrypt(encrypted, privateKey).get // "sensitive data"
// HMAC authentication (SHA-256 and SHA-512)
val hmac256 = Crypto.hmac256("authenticate this", privateKey)
val isValid256 = Crypto.validateHmac256("authenticate this", privateKey, hmac256) // true
val hmac512 = Crypto.hmac512("authenticate this", privateKey)
val isValid512 = Crypto.validateHmac512("authenticate this", privateKey, hmac512) // true
// Base64 encoding/decoding
val encoded = Crypto.base64Encode("hello world")
val decoded = Crypto.base64Decode(encoded) // "hello world"
Runtime Utilities
The runtimes
package provides execution contexts and executor services that use Java 21's virtual threads:
import dev.wishingtree.branch.macaroni.runtimes.BranchExecutors
// Get the global virtual thread executor
val executor = BranchExecutors.executorService
// Get the global execution context
val ec = BranchExecutors.executionContext