Skip to content

Commit

Permalink
Handle duplicate child key errors more gracefully.
Browse files Browse the repository at this point in the history
  • Loading branch information
Laimiux committed Dec 7, 2023
1 parent 6374812 commit 2f8d570
Showing 1 changed file with 56 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.instacart.formula.internal

import com.instacart.formula.IFormula
import com.instacart.formula.Inspector
import java.lang.IllegalStateException

/**
* Keeps track of child formula managers.
Expand All @@ -11,6 +12,7 @@ internal class ChildrenManager(
private val inspector: Inspector?,
) {
private var children: SingleRequestMap<Any, FormulaManager<*, *>>? = null
private var indexes: MutableMap<Any, Int>? = null
private var pendingRemoval: MutableList<FormulaManager<*, *>>? = null

/**
Expand All @@ -19,6 +21,8 @@ internal class ChildrenManager(
* in post evaluation, which will call [terminateChildren] function.
*/
fun prepareForPostEvaluation() {
indexes?.clear()

children?.clearUnrequested {
pendingRemoval = pendingRemoval ?: mutableListOf()
it.markAsTerminated()
Expand Down Expand Up @@ -51,20 +55,64 @@ internal class ChildrenManager(
formula: IFormula<ChildInput, ChildOutput>,
input: ChildInput,
): FormulaManager<ChildInput, ChildOutput> {
val childHolder = childFormulaHolder(key, formula, input)
return if (childHolder.requested) {
if (key is IndexedKey) {
// This should never happen, but added as safety
throw IllegalStateException("Key already indexed (and still duplicate).")
}

// TODO: Notify that duplicate child key detected
// (we should specify explicit keys instead of relying on this fallback).
val index = nextIndex(key)
val indexedKey = IndexedKey(key, index)
findOrInitChild(indexedKey, formula, input)
} else {
childHolder.requestAccess {
"There already is a child with same key: $key. Override [Formula.key] function."
}
}
}

private fun <ChildInput, ChildOutput> childFormulaHolder(
key: Any,
formula: IFormula<ChildInput, ChildOutput>,
input: ChildInput,
): SingleRequestHolder<FormulaManager<ChildInput, ChildOutput>> {
@Suppress("UNCHECKED_CAST")
val children = children ?: run {
val initialized: SingleRequestMap<Any, FormulaManager<*, *>> = LinkedHashMap()
this.children = initialized
initialized
}

return children
.findOrInit(key) {
val implementation = formula.implementation()
FormulaManagerImpl(delegate, implementation, input, loggingType = formula::class, inspector = inspector)
}
.requestAccess {
"There already is a child with same key: $key. Override [Formula.key] function."
} as FormulaManager<ChildInput, ChildOutput>
val childFormulaHolder = children.findOrInit(key) {
val implementation = formula.implementation()
FormulaManagerImpl(
delegate,
implementation,
input,
loggingType = formula::class,
inspector = inspector
)
}
@Suppress("UNCHECKED_CAST")
return childFormulaHolder as SingleRequestHolder<FormulaManager<ChildInput, ChildOutput>>
}

/**
* Function which returns next index for a given key. It will
* mutate the [indexes] map.
*/
private fun nextIndex(key: Any): Int {
val indexes = indexes ?: run {
val initialized = mutableMapOf<Any, Int>()
this.indexes = initialized
initialized
}

val index = indexes.getOrElse(key) { 0 } + 1
indexes[key] = index
return index
}
}

0 comments on commit 2f8d570

Please sign in to comment.