commonMain.internal.ExternalIssuesDependencies.kt Maven / Gradle / Ivy
package io.kform.internal
import io.kform.AbsolutePath
import io.kform.LocatedValidationIssue
import io.kform.Path
import io.kform.collections.mutablePathMultimapOf
import io.kform.collections.set
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
/**
* Class tracking dependencies of a form manager's external issues, providing coroutine-safe methods
* to get, add, and remove them.
*/
internal class ExternalIssuesDependencies {
/** Maps paths to the external issues depending on them. */
private val pathDependencies = mutablePathMultimapOf()
/** Maps external contexts to the external issues depending on them. */
private val externalContextDependencies =
mutableMapOf>()
private val mutex = Mutex()
/** Gets and removes all external issues dependent on [path]. */
suspend fun getAndRemoveExternalIssuesDependentOnPath(
path: AbsolutePath
): Set {
mutex.withLock {
val entriesToRemove = pathDependencies.entries(path).toList()
for (entry in entriesToRemove) {
pathDependencies.removeEntry(entry.id)
}
val removedIssues = entriesToRemove.mapTo(HashSet(entriesToRemove.size)) { it.value }
for (issue in removedIssues) {
removeExternalContextDependencies(issue)
}
return removedIssues
}
}
/** Gets and removes all external issues dependent on the external context [externalContext]. */
suspend fun getAndRemoveExternalIssuesDependentOnExternalContext(
externalContext: String
): Set {
mutex.withLock {
val removedIssues = externalContextDependencies.remove(externalContext) ?: emptySet()
for (issue in removedIssues) {
removePathDependencies(issue)
}
return removedIssues
}
}
/** Adds dependencies from all provided [externalIssues]. */
suspend fun addDependenciesOfExternalIssues(externalIssues: Iterable) {
mutex.withLock {
for (issue in externalIssues) {
val dependencies = pathDependencies(issue)
for (dependency in dependencies) {
pathDependencies[dependency] = issue
}
for (contextDependency in issue.externalContextDependencies) {
externalContextDependencies.getOrPut(contextDependency) { mutableSetOf() } +=
issue
}
}
}
}
/** Removes all dependencies of all provided [externalIssues]. */
suspend fun removeDependenciesOfExternalIssues(
externalIssues: Iterable
) {
mutex.withLock {
for (issue in externalIssues) {
removePathDependencies(issue)
removeExternalContextDependencies(issue)
}
}
}
/**
* Returns the path dependencies of [externalIssue] taking into consideration whether it
* [depends on descendants][LocatedValidationIssue.dependsOnDescendants].
*/
private fun pathDependencies(externalIssue: LocatedValidationIssue): Set {
val dependencies = externalIssue.dependencies.toMutableSet()
if (externalIssue.dependsOnDescendants) {
dependencies += externalIssue.path + Path.DESCENDANTS
}
return dependencies
}
/** Removes all path dependencies of [externalIssue] from [pathDependencies]. */
private fun removePathDependencies(externalIssue: LocatedValidationIssue) {
val dependencies = pathDependencies(externalIssue)
for (dependency in dependencies) {
val entriesToRemove =
pathDependencies.entries(dependency).filter { it.value == externalIssue }.toList()
for (entry in entriesToRemove) {
pathDependencies.removeEntry(entry.id)
}
}
}
/**
* Removes all external context dependencies of [externalIssue] from
* [externalContextDependencies].
*/
private fun removeExternalContextDependencies(externalIssue: LocatedValidationIssue) {
for (externalContext in externalIssue.externalContextDependencies) {
externalContextDependencies[externalContext]?.remove(externalIssue)
}
}
}