Move SSD truth index selection to typed Kotlin

ober

22e7dba32779734d7ab3e27f66d4a07e517ca4e0

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index 2ec1b43..0d3e5dc 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -3157,6 +3157,37 @@
                     "."
                     (string-append (int->string tenths) " s")))))))))
 
+    (typed-kotlin-file "com/sfb/ssdreview/TruthIndexSelect.kt"
+      (typed-library (com sfb ssdreview)
+        (export bestTruthIndexForSession)
+        (type Int32)
+        (type TruthIndexEntry)
+        (type SsdSession)
+        (def (truthIndexMatchesSession? (entry : TruthIndexEntry)
+                                        (session : SsdSession)) : Bool
+          (let ((wantedName (sourceBaseName (SsdSession-sourceName session)))
+                (wantedCanonicalName (canonicalSourceName (SsdSession-sourceName session))))
+            (and (or (equal? (TruthIndexEntry-sourceName entry) wantedName)
+                     (equal? (TruthIndexEntry-canonicalSourceName entry) wantedCanonicalName))
+                 (and (= (TruthIndexEntry-page entry) (SsdSession-page session))
+                      (= (TruthIndexEntry-dpi entry) (SsdSession-dpi session))))))
+        (def (betterNullableTruthIndex (candidate : TruthIndexEntry)
+                                       (current : (Nullable TruthIndexEntry))) : (Nullable TruthIndexEntry)
+          (if (nullable-null? current)
+            (nullable-some candidate)
+            (if (betterTruthIndex candidate (nullable-get current))
+              (nullable-some candidate)
+              current)))
+        (def (bestTruthIndexForSession (entries : (List TruthIndexEntry))
+                                       (initial : (Nullable TruthIndexEntry))
+                                       (session : SsdSession)) : (Nullable TruthIndexEntry)
+          (for/fold ((best initial))
+                    ((i (in-range (int32 0) (list-size entries))))
+            (let ((entry (list-ref entries i)))
+              (if (truthIndexMatchesSession? entry session)
+                (betterNullableTruthIndex entry best)
+                best))))))
+
     (typed-kotlin-file "com/sfb/ssdreview/BoxTypeLabelAndroid.kt"
       (kotlin-imports (android content Context))
       (typed-library (com sfb ssdreview)
@@ -5886,21 +5917,11 @@
        "    }"
        ""
        "    fun loadTruth(session: SsdSession): JSONObject? {"
-       "        var best = truthIndexEntry(truthPath(session.sourceKey))"
-       "        val wantedName = sourceBaseName(session.sourceName)"
-       "        val wantedCanonicalName = canonicalSourceName(session.sourceName)"
-       "        truthIndexEntries().forEach { entry ->"
-       "                if ("
-       "                    (entry.sourceName == wantedName || entry.canonicalSourceName == wantedCanonicalName) &&"
-       "                    entry.page == session.page &&"
-       "                    entry.dpi == session.dpi"
-       "                ) {"
-       "                    val current = best"
-       "                    if (current == null || betterTruthIndex(entry, current)) {"
-       "                        best = entry"
-       "                    }"
-       "                }"
-       "        }"
+       "        val best = bestTruthIndexForSession("
+       "            truthIndexEntries(),"
+       "            truthIndexEntry(truthPath(session.sourceKey)),"
+       "            session"
+       "        )"
        "        return best?.let { JSONObject(readLocalText(it._file)) }"
        "    }"
        ""