Move SSD JSONL limit checks to typed Kotlin

ober

263bb31b5d0d0e4487171d4a0ca8ab968d61394c

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index ce58759..6fce4db 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -1426,6 +1426,7 @@
                 clientLogHttpStatusMessage bearerAuthorizationHeader
                 zipEntryCountLimitExceededMessage
                 httpStatusSuccessful httpStatusFailed httpStatusMessage
+                jsonLineCountExceeded jsonLineCountAtLimit
                 unableToCreateSyncFolderMessage unableToCreateFileMessage)
         (type Int32)
         (record ImportBudget
@@ -1496,6 +1497,10 @@
           (not (httpStatusSuccessful code)))
         (def (httpStatusMessage (code : Int32)) : String
           (string-append "HTTP " (int32->string code)))
+        (def (jsonLineCountExceeded (lines : Int32) (limit : Int32)) : Bool
+          (> lines limit))
+        (def (jsonLineCountAtLimit (lines : Int32) (limit : Int32)) : Bool
+          (>= lines limit))
         (def (unableToCreateSyncFolderMessage (name : String)) : String
           (string-append "Unable to create sync folder " name))
         (def (unableToCreateFileMessage (fileName : String)) : String
@@ -7552,7 +7557,7 @@
       (kotlin-imports (android graphics Bitmap))
       (typed-library (com sfb ssdreview)
         (export bitmapPixelWidth bitmapPixelHeight bitmapHasPixels
-                bitmapReadyForNativeDetection
+                bitmapReadyForNativeDetection bitmapNotReadyForNativeDetection
                 bitmapPixels bitmapWindowPixels bitmapToRgba)
         (type Bitmap)
         (type Int32)
@@ -7580,6 +7585,8 @@
                (> (bitmapPixelHeight bitmap) (int32 0))))
         (def (bitmapReadyForNativeDetection (loaded : Bool) (bitmap : Bitmap)) : Bool
           (and loaded (bitmapHasPixels bitmap)))
+        (def (bitmapNotReadyForNativeDetection (loaded : Bool) (bitmap : Bitmap)) : Bool
+          (not (bitmapReadyForNativeDetection loaded bitmap)))
         (def (bitmapWindowPixels (bitmap : Bitmap)
                                  (x : Int32)
                                  (y : Int32)
@@ -7757,7 +7764,7 @@
        "    fun loadErrorMessage(): String = loadError?.message ?: \"\""
        ""
        "    fun detect(bitmap: Bitmap): NativeDetection? {"
-       "        if (!bitmapReadyForNativeDetection(loaded, bitmap)) return null"
+       "        if (bitmapNotReadyForNativeDetection(loaded, bitmap)) return null"
        "        val rgba = bitmapToRgba(bitmap)"
        "        val jsonText = try {"
        "            nativeDetectRgbaJson(bitmapPixelWidth(bitmap), bitmapPixelHeight(bitmap), rgba)"
@@ -8831,7 +8838,7 @@
        "            var lines = 0"
        "            trimmedNonBlankLines(text).forEach { line ->"
        "                lines += 1"
-       "                if (lines > MAX_JSONL_LINES) throw IllegalStateException(\"JSONL line-count limit exceeded\")"
+       "                if (jsonLineCountExceeded(lines, MAX_JSONL_LINES)) throw IllegalStateException(\"JSONL line-count limit exceeded\")"
        "                jsonObjectFromText(line)"
        "            }"
        "        } else {"
@@ -9041,7 +9048,7 @@
        "            trimmedNonBlankLines(readLocalText(file)).forEach { lines.add(it) }"
        "        }"
        "        trimmedNonBlankLines(remoteText).forEach {"
-       "            if (lines.size >= MAX_JSONL_LINES) throw IllegalStateException(\"JSONL line-count limit exceeded\")"
+       "            if (jsonLineCountAtLimit(lines.size, MAX_JSONL_LINES)) throw IllegalStateException(\"JSONL line-count limit exceeded\")"
        "            lines.add(it)"
        "        }"
        "        val merged = mergedJsonLinesText(lines)"