From 6fedc115aa9f8e732bf91967977bb8ab027b4386 Mon Sep 17 00:00:00 2001 From: Lanius Trolling Date: Tue, 9 Apr 2024 12:19:38 -0400 Subject: [PATCH] Add serial transfer --- build.gradle.kts | 12 ++ .../info/mechyrdia/data/MigrateFilesSerial.kt | 103 ++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 src/jvmMain/kotlin/info/mechyrdia/data/MigrateFilesSerial.kt diff --git a/build.gradle.kts b/build.gradle.kts index c9f6d7f..c9753d9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -287,6 +287,18 @@ tasks.register("migrateToGridFs", JavaExec::class) { setArgs(listOf("config", "gridfs")) } +tasks.register("migrateToGridFsSerial", JavaExec::class) { + group = "administration" + + val runShadow: JavaExec by tasks + val main by sourceSets + + javaLauncher.convention(runShadow.javaLauncher) + classpath = main.runtimeClasspath + mainClass.set("info.mechyrdia.data.MigrateFilesSerial") + setArgs(listOf("config", "gridfs")) +} + tasks.withType { javaLauncher.set(javaToolchains.launcherFor { languageVersion.set(JavaLanguageVersion.of(17)) diff --git a/src/jvmMain/kotlin/info/mechyrdia/data/MigrateFilesSerial.kt b/src/jvmMain/kotlin/info/mechyrdia/data/MigrateFilesSerial.kt new file mode 100644 index 0000000..784b6bc --- /dev/null +++ b/src/jvmMain/kotlin/info/mechyrdia/data/MigrateFilesSerial.kt @@ -0,0 +1,103 @@ +@file:JvmName("MigrateFilesSerial") + +package info.mechyrdia.data + +import info.mechyrdia.Configuration +import info.mechyrdia.FileStorageConfig +import kotlinx.coroutines.runBlocking +import kotlin.system.exitProcess + +private fun printUsage(): Nothing { + println("Usage: ") + println("Both arguments are of either following format:") + println(" gridfs - use GridFS (database connection indicated by config.json)") + println(" config - storage indicated in config file") + println(" file: - use flat-file storage") + exitProcess(-1) +} + +private fun String.parseStorage(): FileStorageConfig { + val configuration = Configuration.Current + + return if (this == "config") + configuration.storage + else if (this == "gridfs") + FileStorageConfig.GridFs + else if (startsWith("file:")) + FileStorageConfig.Flat(removePrefix("file:")) + else { + println("Invalid format for argument value $this") + printUsage() + } +} + +private suspend fun migrateFile(path: StoragePath, from: FileStorage, into: FileStorage): List { + println("[Message] Starting transfer of /$path") + + val bytes = from.readFile(path) ?: return listOf("[Source Error] File does not exist at /$path") + if (!into.writeFile(path, bytes)) + return listOf("[Target Error] File at /$path cannot be written to") + + println("[Message] Done transferring /$path") + return emptyList() +} + +private suspend fun migrateDir(path: StoragePath, from: FileStorage, into: FileStorage): List { + if (!into.createDir(path)) + return listOf("[Target Error] Directory at /$path cannot be created") + + val inDir = from.listDir(path) ?: return listOf("[Source Error] Directory at /$path does not exist") + + return inDir.flatMap { entry -> + val entryPath = path / entry.name + when (entry.type) { + StoredFileType.FILE -> migrateFile(entryPath, from, into) + StoredFileType.DIRECTORY -> migrateDir(entryPath, from, into) + } + } +} + +private suspend fun migrateRoot(from: FileStorage, into: FileStorage): List { + val inRoot = from.listDir(StoragePath.Root) + ?: return listOf("[Source Error] Root directory does not exist") + + return inRoot.flatMap { entry -> + val entryPath = StoragePath.Root / entry.name + when (entry.type) { + StoredFileType.FILE -> migrateFile(entryPath, from, into) + StoredFileType.DIRECTORY -> migrateDir(entryPath, from, into) + } + } +} + +fun main(args: Array) { + if (args.size != 2) { + println("Invalid number of arguments ${args.size}, expected 2") + printUsage() + } + + val (from, into) = args.map { it.parseStorage() } + if (from == into) { + println("Cannot migrate storage to itself") + printUsage() + } + + val errors = runBlocking { + System.setProperty("logback.statusListenerClass", "ch.qos.logback.core.status.NopStatusListener") + + ConnectionHolder.initialize(Configuration.Current.dbConn, Configuration.Current.dbName) + + val fromStorage = FileStorage(from) + val intoStorage = FileStorage(into) + + migrateRoot(fromStorage, intoStorage) + } + + if (errors.isEmpty()) + println("Successful migration! No errors encountered!") + else { + println("Migration encountered ${errors.size} ${errors.size.pluralize("error")}") + for (error in errors) + println(error) + } +} -- 2.25.1