Generate SSD truth store class from typed Jerboa

ober

6267cf0e0b74d59b7ba8f97dc57cee7b1f935501

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index 54730f5..24dcece 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -18533,235 +18533,262 @@
                       (truthStoreInputStreamClose input)))))
               (truthStoreConnectionDisconnect connection))))))
 
-    (kotlin-file-lines "com/sfb/ssdreview/TruthStore.kt"
-      (
-       "package com.sfb.ssdreview"
-       ""
-       "import android.content.Context"
-       "import android.net.Uri"
-       "import android.util.Base64"
-       "import androidx.documentfile.provider.DocumentFile"
-       "import org.json.JSONArray"
-       "import org.json.JSONObject"
-       "import java.io.BufferedInputStream"
-       "import java.io.BufferedOutputStream"
-       "import java.io.ByteArrayOutputStream"
-       "import java.io.File"
-       "import java.io.FilterInputStream"
-       "import java.io.FilterOutputStream"
-       "import java.io.InputStream"
-       "import java.io.OutputStream"
-       "import java.net.URL"
-       "import java.nio.channels.FileChannel"
-       "import java.nio.file.Files"
-       "import java.nio.file.LinkOption"
-       "import java.nio.file.StandardCopyOption"
-       "import java.nio.file.StandardOpenOption"
-       "import java.security.KeyStore"
-       "import java.security.MessageDigest"
-       "import java.security.SecureRandom"
-       "import java.security.cert.CertificateException"
-       "import java.security.cert.X509Certificate"
-       "import java.util.zip.ZipEntry"
-       "import java.util.zip.GZIPInputStream"
-       "import java.util.zip.ZipInputStream"
-       "import java.util.zip.ZipOutputStream"
-       "import javax.net.ssl.HttpsURLConnection"
-       "import javax.net.ssl.SSLContext"
-       "import javax.net.ssl.SSLPeerUnverifiedException"
-       "import javax.net.ssl.SSLSocketFactory"
-       "import javax.net.ssl.TrustManager"
-       "import javax.net.ssl.TrustManagerFactory"
-       "import javax.net.ssl.X509TrustManager"
-       "import kotlin.concurrent.thread"
-       ""
-       "class TruthStore(internal val context: Context) {"
-       "    internal val root = File(context.filesDir, \"ssd_review\")"
-       "    internal val groundTruthDir = File(root, \"ground_truth\")"
-       "    internal val learnedDir = File(root, \"learned\")"
-       "    internal val eventsDir = File(root, \"events\")"
-       "    internal val reviewsDir = File(root, \"reviews\")"
-       "    internal val remoteConfig: RemoteConfig? by lazy { loadRemoteConfig() }"
-       "    @Volatile internal var seedStarted = false"
-       "    @Volatile internal var truthIndexCacheLoaded = false"
-       "    @Volatile internal var truthIndexCache: MutableList<TruthIndexEntry> = ArrayList()"
-       ""
-       "    init {"
-       "        ensureTruthStoreDirectories(root, groundTruthDir, learnedDir, eventsDir, reviewsDir)"
-       "    }"
-       ""
-       "    fun truthPath(sourceKey: String): File ="
-       "        truthStoreTruthPath(this, sourceKey)"
-       ""
-       "    fun loadTruth(sourceKey: String): JSONObject? ="
-       "        truthStoreLoadTruth(this, sourceKey)"
-       ""
-       "    fun loadTruth(session: SsdSession): JSONObject? ="
-       "        truthStoreLoadTruthForSessionLocal(this, session)"
-       ""
-       "    @Synchronized"
-       "    internal fun invalidateTruthIndex() ="
-       "        truthStoreInvalidateTruthIndexLocal(this)"
-       ""
-       "    @Synchronized"
-       "    private fun truthIndexEntries(): List<TruthIndexEntry> ="
-       "        truthStoreTruthIndexEntriesLocal(this)"
-       ""
-       "    private fun truthIndexEntry(file: File): TruthIndexEntry? ="
-       "        truthStoreTruthIndexEntryLocal(this, file)"
-       ""
-       "    fun saveSession(session: SsdSession): File ="
-       "        truthStoreSaveSession(this, session)"
-       ""
-       "    @Synchronized"
-       "    fun saveTruthSnapshot(truth: JSONObject): File ="
-       "        truthStoreSaveTruthSnapshotLocal(this, truth)"
-       ""
-       "    fun appendEvent(type: String, session: SsdSession, details: JSONObject = emptyJsonObject()) ="
-       "        truthStoreAppendEvent(this, type, session, details)"
-       ""
-       "    fun applyTruth(session: SsdSession) ="
-       "        truthStoreApplyTruth(this, session)"
-       ""
-       "    private fun sha256Hex(bytes: ByteArray): String ="
-       "        truthStoreSha256Hex(bytes)"
-       ""
-       "    private fun containedLeaf(base: File, leaf: String): File ="
-       "        truthStoreContainedLeaf(base, leaf)"
-       ""
-       "    private fun storageStats(): StorageStats ="
-       "        truthStoreStorageStatsLocal(this)"
-       ""
-       "    private fun ensureStorageCapacity(destination: File, bytes: Long) ="
-       "        truthStoreEnsureStorageCapacityLocal(this, destination, bytes)"
-       ""
-       "    internal fun atomicWrite(destination: File, bytes: ByteArray) ="
-       "        truthStoreAtomicWriteLocal(this, destination, bytes)"
-       ""
-       "    internal fun atomicWriteText(destination: File, text: String) ="
-       "        atomicWrite(destination, textUtf8Bytes(text))"
-       ""
-       "    private fun loadRemoteConfig(): RemoteConfig? ="
-       "        truthStoreLoadRemoteConfigLocal(context)"
-       ""
-       "    private fun remoteUrl(relative: String): URL ="
-       "        truthStoreRemoteUrlLocal(remoteConfig, relative)"
-       ""
-       "    internal fun openPinnedConnection("
-       "        relative: String,"
-       "        method: String,"
-       "        outputBytes: Long? = null,"
-       "        contentType: String? = null,"
-       "        acceptGzip: Boolean = false"
-       "    ): HttpsURLConnection ="
-       "        truthStoreOpenPinnedConnectionLocal(this, relative, method, outputBytes, contentType, acceptGzip)"
-       ""
-       "    internal fun pinnedSocketFactory(config: RemoteConfig): SSLSocketFactory ="
-       "        truthStorePinnedSocketFactoryLocal(config)"
-       ""
-       "    fun pullRemoteTruth(session: SsdSession): Int ="
-       "        truthStorePullRemoteTruthLocal(this, session)"
-       ""
-       "    private fun readResponseText(connection: HttpsURLConnection): String ="
-       "        truthStoreReadResponseTextLocal(connection)"
-       ""
-       "    private fun writeRemoteTruth("
-       "        truth: JSONObject,"
-       "        modified: Long,"
-       "        replaceIfMoreComplete: Boolean = false"
-       "    ): Boolean ="
-       "        truthStoreWriteRemoteTruthLocal(this, truth, modified, replaceIfMoreComplete)"
-       ""
-       "    fun applyLearnedGuesses(session: SsdSession): Int ="
-       "        truthStoreApplyLearnedGuessesLocal(this, session)"
-       ""
-       "    fun exportZip(out: OutputStream) ="
-       "        truthStoreExportZipLocal(this, out)"
-       ""
-       "    fun importZip(input: InputStream): Int ="
-       "        truthStoreImportZipLocal(this, input)"
-       ""
-       "    private fun streamZipEntry("
-       "        zip: ZipInputStream,"
-       "        destination: File,"
-       "        compressed: LimitedInputStream,"
-       "        budget: ImportBudget"
-       "    ) ="
-       "        truthStoreStreamZipEntryLocal(zip, destination, compressed, budget)"
-       ""
-       "    private fun installStagedZipEntry(entry: StagedZipEntry): Boolean ="
-       "        truthStoreInstallStagedZipEntryLocal(this, entry)"
-       ""
-       "    private fun validateImportedFile(name: String, file: File) ="
-       "        truthStoreValidateImportedFileLocal(name, file)"
-       ""
-       "    private fun validateImportedText(name: String, text: String) ="
-       "        truthStoreValidateImportedTextLocal(name, text)"
-       ""
-       "    fun syncTree(treeUri: Uri): Pair<Int, Int> ="
-       "        truthStoreSyncTreeLocal(this, treeUri)"
-       ""
-       "    fun pullRemoteDump(): Int ="
-       "        truthStorePullRemoteDumpLocal(this)"
-       ""
-       "    fun pushRemoteDump(): Int ="
-       "        truthStorePushRemoteDumpLocal(this)"
-       ""
-       "    fun storageSummary(): String ="
-       "        truthStoreStorageSummaryLocal(this)"
-       ""
-       "    fun seedBundledTruthAsync(done: (Int) -> Unit) {"
-       "        truthStoreSeedBundledTruthAsync(this, done)"
-       "    }"
-       ""
-       "    internal fun rebuildLearnedExamples() ="
-       "        truthStoreRebuildLearnedExamplesLocal(this)"
-       ""
-       "    internal fun seedBundledTruth(): Int ="
-       "        truthStoreSeedBundledTruthLocal(this, context)"
-       ""
-       "    private fun exportToTree(tree: DocumentFile): Int ="
-       "        truthStoreExportToTreeLocal(this, tree)"
-       ""
-       "    private fun importFromTree(tree: DocumentFile): Int ="
-       "        truthStoreImportFromTreeLocal(this, tree)"
-       ""
-       "    private fun importTreeDir("
-       "        sourceDir: DocumentFile,"
-       "        localDir: File,"
-       "        depth: Int,"
-       "        budget: ImportBudget"
-       "    ): Int ="
-       "        truthStoreImportTreeDirLocal(this, sourceDir, localDir, depth, budget)"
-       ""
-       "    private fun ensureTreeDir(parent: DocumentFile, name: String): DocumentFile ="
-       "        truthStoreEnsureTreeDirLocal(parent, name)"
-       ""
-       "    private fun writeTreeFile(parent: DocumentFile, relativeName: String, source: File) ="
-       "        truthStoreWriteTreeFileLocal(this, parent, relativeName, source)"
-       ""
-       "    private fun mergeJsonLines(file: File, remoteText: String) ="
-       "        truthStoreMergeJsonLinesLocal(this, file, remoteText)"
-       ""
-       "    internal fun shouldReplaceLocal(local: File, remoteText: String, remoteModified: Long): Boolean ="
-       "        truthStoreShouldReplaceLocal(local, remoteText, remoteModified)"
-       ""
-       "    private fun truthTime(truth: JSONObject): Long? ="
-       "        truthStoreTruthTimeLocal(truth)"
-       ""
-       "    internal fun appendJsonLine(file: File, json: JSONObject) ="
-       "        truthStoreAppendJsonLineLocal(this, file, json)"
-       ""
-       "    internal fun pushTruthAsync(truth: JSONObject) ="
-       "        truthStorePushTruthAsyncLocal(this, truth)"
-       ""
-       "    private fun containedImportPath(name: String): File ="
-       "        truthStoreContainedImportPathLocal(this, name)"
-       ""
-       "    private fun readLocalText(file: File): String ="
-       "        truthStoreReadLocalText(file)"
-       ""
-       "}"
-       ""
-       ))
+    (typed-kotlin-file "com/sfb/ssdreview/TruthStore.kt"
+      (kotlin-imports (android content Context)
+                      (android net Uri)
+                      (androidx documentfile provider DocumentFile)
+                      (java io File)
+                      (java io InputStream)
+                      (java io OutputStream)
+                      (java net URL)
+                      (java util zip ZipInputStream)
+                      (javax net ssl HttpsURLConnection)
+                      (javax net ssl SSLSocketFactory)
+                      (org json JSONObject))
+      (typed-library (com sfb ssdreview)
+        (export TruthStore)
+        (type Bytes)
+        (type Context)
+        (type DocumentFile)
+        (type File)
+        (type HttpsURLConnection)
+        (type ImportBudget)
+        (type InputStream)
+        (type Int32)
+        (type LimitedInputStream)
+        (type OutputStream)
+        (type RemoteConfig)
+        (type SSLSocketFactory)
+        (type SsdSession)
+        (type StagedZipEntry)
+        (type StorageStats)
+        (type TruthIndexEntry)
+        (type Uri)
+        (type URL)
+        (type JSONObject)
+        (type ZipInputStream)
+        (extern (truthStoreClassContextFilesDir
+                  (context : Context)) : File
+          (kotlin-member-get filesDir))
+        (class TruthStore
+          ((context : Context (property val) (visibility internal)))
+          (val root : File
+            (new File (truthStoreClassContextFilesDir context) "ssd_review")
+            (modifiers internal))
+          (val groundTruthDir : File
+            (new File root "ground_truth")
+            (modifiers internal))
+          (val learnedDir : File
+            (new File root "learned")
+            (modifiers internal))
+          (val eventsDir : File
+            (new File root "events")
+            (modifiers internal))
+          (val reviewsDir : File
+            (new File root "reviews")
+            (modifiers internal))
+          (val remoteConfig : (Nullable RemoteConfig)
+            (truthStoreLoadRemoteConfigLocal context)
+            (modifiers internal))
+          (var seedStarted : Bool
+            #f
+            (modifiers @Volatile internal))
+          (var truthIndexCacheLoaded : Bool
+            #f
+            (modifiers @Volatile internal))
+          (var truthIndexCache : (MutableList TruthIndexEntry)
+            (mutable-list-empty TruthIndexEntry)
+            (modifiers @Volatile internal))
+          (init
+            (ensureTruthStoreDirectories
+              root
+              groundTruthDir
+              learnedDir
+              eventsDir
+              reviewsDir))
+          (def (truthPath (sourceKey : String)) : File
+            (truthStoreTruthPath this sourceKey))
+          (def (loadTruthSource (sourceKey : String)) : (Nullable JSONObject)
+            (kotlin-name loadTruth)
+            (truthStoreLoadTruth this sourceKey))
+          (def (loadTruthSession (session : SsdSession)) : (Nullable JSONObject)
+            (kotlin-name loadTruth)
+            (truthStoreLoadTruthForSessionLocal this session))
+          (def (invalidateTruthIndex) : Unit
+            (modifiers @Synchronized internal)
+            (truthStoreInvalidateTruthIndexLocal this))
+          (def (truthIndexEntries) : (List TruthIndexEntry)
+            (modifiers @Synchronized private)
+            (truthStoreTruthIndexEntriesLocal this))
+          (def (truthIndexEntry (file : File)) : (Nullable TruthIndexEntry)
+            (modifiers private)
+            (truthStoreTruthIndexEntryLocal this file))
+          (def (saveSession (session : SsdSession)) : File
+            (truthStoreSaveSession this session))
+          (def (saveTruthSnapshot (truth : JSONObject)) : File
+            (modifiers @Synchronized)
+            (truthStoreSaveTruthSnapshotLocal this truth))
+          (def (appendEvent
+                 (type : String)
+                 (session : SsdSession)
+                 (details : JSONObject (default (emptyJsonObject)))) : Unit
+            (truthStoreAppendEvent this type session details))
+          (def (applyTruth (session : SsdSession)) : Unit
+            (truthStoreApplyTruth this session))
+          (def (sha256Hex (bytes : Bytes)) : String
+            (modifiers private)
+            (truthStoreSha256Hex bytes))
+          (def (containedLeaf (base : File) (leaf : String)) : File
+            (modifiers private)
+            (truthStoreContainedLeaf base leaf))
+          (def (storageStats) : StorageStats
+            (modifiers private)
+            (truthStoreStorageStatsLocal this))
+          (def (ensureStorageCapacity
+                 (destination : File)
+                 (bytes : Int)) : Unit
+            (modifiers private)
+            (truthStoreEnsureStorageCapacityLocal this destination bytes))
+          (def (atomicWrite (destination : File) (bytes : Bytes)) : Unit
+            (modifiers internal)
+            (truthStoreAtomicWriteLocal this destination bytes))
+          (def (atomicWriteText (destination : File) (text : String)) : Unit
+            (modifiers internal)
+            (truthStoreAtomicWriteLocal this destination (textUtf8Bytes text)))
+          (def (loadRemoteConfig) : (Nullable RemoteConfig)
+            (modifiers private)
+            (truthStoreLoadRemoteConfigLocal context))
+          (def (remoteUrl (relative : String)) : URL
+            (modifiers private)
+            (truthStoreRemoteUrlLocal remoteConfig relative))
+          (def (openPinnedConnection
+                 (relative : String)
+                 (method : String)
+                 (outputBytes : (Nullable Int) (default (nullable-none Int)))
+                 (contentType : (Nullable String) (default (nullable-none String)))
+                 (acceptGzip : Bool (default #f))) : HttpsURLConnection
+            (modifiers internal)
+            (truthStoreOpenPinnedConnectionLocal
+              this
+              relative
+              method
+              outputBytes
+              contentType
+              acceptGzip))
+          (def (pinnedSocketFactory
+                 (config : RemoteConfig)) : SSLSocketFactory
+            (modifiers internal)
+            (truthStorePinnedSocketFactoryLocal config))
+          (def (pullRemoteTruth (session : SsdSession)) : Int32
+            (truthStorePullRemoteTruthLocal this session))
+          (def (readResponseText
+                 (connection : HttpsURLConnection)) : String
+            (modifiers private)
+            (truthStoreReadResponseTextLocal connection))
+          (def (writeRemoteTruth
+                 (truth : JSONObject)
+                 (modified : Int)
+                 (replaceIfMoreComplete : Bool (default #f))) : Bool
+            (modifiers private)
+            (truthStoreWriteRemoteTruthLocal
+              this
+              truth
+              modified
+              replaceIfMoreComplete))
+          (def (applyLearnedGuesses (session : SsdSession)) : Int32
+            (truthStoreApplyLearnedGuessesLocal this session))
+          (def (exportZip (out : OutputStream)) : Unit
+            (truthStoreExportZipLocal this out))
+          (def (importZip (input : InputStream)) : Int32
+            (truthStoreImportZipLocal this input))
+          (def (streamZipEntry
+                 (zip : ZipInputStream)
+                 (destination : File)
+                 (compressed : LimitedInputStream)
+                 (budget : ImportBudget)) : Unit
+            (modifiers private)
+            (truthStoreStreamZipEntryLocal
+              zip
+              destination
+              compressed
+              budget))
+          (def (installStagedZipEntry (entry : StagedZipEntry)) : Bool
+            (modifiers private)
+            (truthStoreInstallStagedZipEntryLocal this entry))
+          (def (validateImportedFile (name : String) (file : File)) : Unit
+            (modifiers private)
+            (truthStoreValidateImportedFileLocal name file))
+          (def (validateImportedText (name : String) (text : String)) : Unit
+            (modifiers private)
+            (truthStoreValidateImportedTextLocal name text))
+          (def (syncTree (treeUri : Uri)) : (Pair Int32 Int32)
+            (truthStoreSyncTreeLocal this treeUri))
+          (def (pullRemoteDump) : Int32
+            (truthStorePullRemoteDumpLocal this))
+          (def (pushRemoteDump) : Int32
+            (truthStorePushRemoteDumpLocal this))
+          (def (storageSummary) : String
+            (truthStoreStorageSummaryLocal this))
+          (def (seedBundledTruthAsync
+                 (done : (-> Int32 Unit))) : Unit
+            (truthStoreSeedBundledTruthAsync this done))
+          (def (rebuildLearnedExamples) : Unit
+            (modifiers internal)
+            (truthStoreRebuildLearnedExamplesLocal this))
+          (def (seedBundledTruth) : Int32
+            (modifiers internal)
+            (truthStoreSeedBundledTruthLocal this context))
+          (def (exportToTree (tree : DocumentFile)) : Int32
+            (modifiers private)
+            (truthStoreExportToTreeLocal this tree))
+          (def (importFromTree (tree : DocumentFile)) : Int32
+            (modifiers private)
+            (truthStoreImportFromTreeLocal this tree))
+          (def (importTreeDir
+                 (sourceDir : DocumentFile)
+                 (localDir : File)
+                 (depth : Int32)
+                 (budget : ImportBudget)) : Int32
+            (modifiers private)
+            (truthStoreImportTreeDirLocal
+              this
+              sourceDir
+              localDir
+              depth
+              budget))
+          (def (ensureTreeDir
+                 (parent : DocumentFile)
+                 (name : String)) : DocumentFile
+            (modifiers private)
+            (truthStoreEnsureTreeDirLocal parent name))
+          (def (writeTreeFile
+                 (parent : DocumentFile)
+                 (relativeName : String)
+                 (source : File)) : Unit
+            (modifiers private)
+            (truthStoreWriteTreeFileLocal this parent relativeName source))
+          (def (mergeJsonLines (file : File) (remoteText : String)) : Unit
+            (modifiers private)
+            (truthStoreMergeJsonLinesLocal this file remoteText))
+          (def (shouldReplaceLocal
+                 (local : File)
+                 (remoteText : String)
+                 (remoteModified : Int)) : Bool
+            (modifiers internal)
+            (truthStoreShouldReplaceLocal local remoteText remoteModified))
+          (def (truthTime (truth : JSONObject)) : (Nullable Int)
+            (modifiers private)
+            (truthStoreTruthTimeLocal truth))
+          (def (appendJsonLine (file : File) (json : JSONObject)) : Unit
+            (modifiers internal)
+            (truthStoreAppendJsonLineLocal this file json))
+          (def (pushTruthAsync (truth : JSONObject)) : Unit
+            (modifiers internal)
+            (truthStorePushTruthAsyncLocal this truth))
+          (def (containedImportPath (name : String)) : File
+            (modifiers private)
+            (truthStoreContainedImportPathLocal this name))
+          (def (readLocalText (file : File)) : String
+            (modifiers private)
+            (truthStoreReadLocalText file)))))
     ))
diff --git a/tests/ssd-security-test.sh b/tests/ssd-security-test.sh
index 5825cc2..92f62e0 100755
--- a/tests/ssd-security-test.sh
+++ b/tests/ssd-security-test.sh
@@ -46,8 +46,8 @@ reject_text() {
     fi
 }
 
-require_text 'HttpsURLConnection'
-require_text 'X509TrustManager'
+require_generated_text 'HttpsURLConnection'
+require_generated_text 'X509TrustManager'
 require_generated_text 'MessageDigest.isEqual'
 require_generated_text 'setRequestProperty("Authorization", bearerAuthorizationHeader(config.bearerToken))'
 require_generated_text 'context.getSharedPreferences("ssd_review_remote", Context.MODE_PRIVATE)'