All checks were successful
Main Workflow / Lint and test library (push) Successful in 16m40s
- [x] Implements library according to instructions, with tests. - [x] GitHub Actions workflow to lint and test library. Reviewed-on: #1
92 lines
2.8 KiB
Kotlin
92 lines
2.8 KiB
Kotlin
package tinyvm
|
|
|
|
class HashCollisionException(hash: String) : Exception("Different object types with identical hash '$hash'")
|
|
|
|
class CommitTimeComparator : Comparator<Commit> {
|
|
override fun compare(
|
|
o1: Commit,
|
|
o2: Commit,
|
|
): Int = (o1.timestamp.epochSecond - o2.timestamp.epochSecond).toInt()
|
|
}
|
|
|
|
class Repository {
|
|
private val commits = sortedSetOf(CommitTimeComparator())
|
|
|
|
// Store all objects in one map like git does. This would simplify the data persistence implementation (if there was
|
|
// one) and allows for other objects to be added in the future without modifying the data persistence implementation
|
|
// at all.
|
|
private val objects = mutableMapOf<String, Object>()
|
|
|
|
/**
|
|
* (Deep) get or put a commit object into the repository.
|
|
* This will also get or put all child trees and blobs.
|
|
*/
|
|
fun commit(commit: Commit): Commit {
|
|
val hash = commit.hash()
|
|
val obj = findObject<Commit>(hash)
|
|
if (obj != null) return obj
|
|
val newCommit =
|
|
Commit(
|
|
tree = addTree(commit.tree),
|
|
author = commit.author,
|
|
message = commit.message,
|
|
timestamp = commit.timestamp,
|
|
)
|
|
objects[hash] = newCommit
|
|
commits.add(newCommit)
|
|
return newCommit
|
|
}
|
|
|
|
/**
|
|
* Get a commit by its hash.
|
|
*/
|
|
fun getCommit(hash: String): Commit? = findObject<Commit>(hash)
|
|
|
|
/**
|
|
* List all commits.
|
|
*/
|
|
fun listCommits(): List<Commit> = commits.toList()
|
|
|
|
/**
|
|
* Find commit.
|
|
*/
|
|
fun findCommit(predicate: (Commit) -> Boolean): Commit? = commits.find(predicate)
|
|
|
|
/**
|
|
* Dump repository objects
|
|
*/
|
|
fun dumpObjects(): Map<String, Object> = objects
|
|
|
|
/**
|
|
* (Deep) get or put a tree object into the repository.
|
|
* This will also get or put all child trees and blobs.
|
|
*/
|
|
private fun addTree(tree: Tree): Tree {
|
|
val hash = tree.hash()
|
|
val obj = findObject<Tree>(hash)
|
|
if (obj != null) return obj
|
|
val newTree =
|
|
Tree(
|
|
tree.nodes.map { (name, node) ->
|
|
when (node) {
|
|
is Tree -> name to addTree(node)
|
|
is Blob -> name to addObject(node)
|
|
}
|
|
}.toMap(),
|
|
)
|
|
objects[hash] = newTree
|
|
return tree
|
|
}
|
|
|
|
/**
|
|
* (Shallow) get or put an object into the repository.
|
|
*/
|
|
private inline fun <reified T : Object> addObject(obj: T): T =
|
|
objects.getOrPut(obj.hash()) { obj } as? T ?: throw HashCollisionException(obj.hash())
|
|
|
|
/**
|
|
* Find an object in the repository by its hash.
|
|
*/
|
|
private inline fun <reified T : Object> findObject(hash: String): T? =
|
|
objects[hash]?.let { it as? T ?: throw HashCollisionException(hash) }
|
|
} |