-
Notifications
You must be signed in to change notification settings - Fork 532
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
282 additions
and
114 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
app/src/main/java/mihon/core/migration/MigrationCompletedListener.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package mihon.core.migration | ||
|
||
typealias MigrationCompletedListener = () -> Unit |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
app/src/main/java/mihon/core/migration/MigrationJobFactory.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package mihon.core.migration | ||
|
||
import kotlinx.coroutines.CompletableDeferred | ||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.Deferred | ||
import kotlinx.coroutines.async | ||
import tachiyomi.core.common.util.system.logcat | ||
|
||
class MigrationJobFactory( | ||
private val migrationContext: MigrationContext, | ||
private val scope: CoroutineScope | ||
) { | ||
|
||
@SuppressWarnings("MaxLineLength") | ||
fun create(migrations: List<Migration>): Deferred<Boolean> = with(scope) { | ||
return migrations.sortedBy { it.version } | ||
.fold(CompletableDeferred(true)) { acc: Deferred<Boolean>, migration: Migration -> | ||
if (!migrationContext.dryrun) { | ||
logcat { "Running migration: { name = ${migration::class.simpleName}, version = ${migration.version} }" } | ||
async { | ||
val prev = acc.await() | ||
migration(migrationContext) || prev | ||
} | ||
} else { | ||
logcat { "(Dry-run) Running migration: { name = ${migration::class.simpleName}, version = ${migration.version} }" } | ||
CompletableDeferred(true) | ||
} | ||
} | ||
} | ||
} |
55 changes: 55 additions & 0 deletions
55
app/src/main/java/mihon/core/migration/MigrationStrategy.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package mihon.core.migration | ||
|
||
import kotlinx.coroutines.CompletableDeferred | ||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.Deferred | ||
import kotlinx.coroutines.launch | ||
|
||
interface MigrationStrategy { | ||
operator fun invoke(migrations: List<Migration>): Deferred<Boolean> | ||
} | ||
|
||
class DefaultMigrationStrategy( | ||
private val migrationJobFactory: MigrationJobFactory, | ||
private val migrationCompletedListener: MigrationCompletedListener, | ||
private val scope: CoroutineScope | ||
) : MigrationStrategy { | ||
|
||
override operator fun invoke(migrations: List<Migration>): Deferred<Boolean> = with(scope) { | ||
if (migrations.isEmpty()) { | ||
return@with CompletableDeferred(false) | ||
} | ||
|
||
val chain = migrationJobFactory.create(migrations) | ||
|
||
launch { | ||
if (chain.await()) migrationCompletedListener() | ||
}.start() | ||
|
||
chain | ||
} | ||
} | ||
|
||
class InitialMigrationStrategy(private val strategy: DefaultMigrationStrategy) : MigrationStrategy { | ||
|
||
override operator fun invoke(migrations: List<Migration>): Deferred<Boolean> { | ||
return strategy(migrations.filter { it.isAlways }) | ||
} | ||
} | ||
|
||
class NoopMigrationStrategy(val state: Boolean) : MigrationStrategy { | ||
|
||
override fun invoke(migrations: List<Migration>): Deferred<Boolean> { | ||
return CompletableDeferred(state) | ||
} | ||
} | ||
|
||
class VersionRangeMigrationStrategy( | ||
private val versions: IntRange, | ||
private val strategy: DefaultMigrationStrategy | ||
) : MigrationStrategy { | ||
|
||
override operator fun invoke(migrations: List<Migration>): Deferred<Boolean> { | ||
return strategy(migrations.filter { it.isAlways || it.version.toInt() in versions }) | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
app/src/main/java/mihon/core/migration/MigrationStrategyFactory.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package mihon.core.migration | ||
|
||
class MigrationStrategyFactory( | ||
private val factory: MigrationJobFactory, | ||
private val migrationCompletedListener: MigrationCompletedListener, | ||
) { | ||
|
||
fun create(old: Int, new: Int): MigrationStrategy { | ||
val versions = (old + 1)..new | ||
val strategy = when { | ||
old == 0 -> InitialMigrationStrategy( | ||
strategy = DefaultMigrationStrategy(factory, migrationCompletedListener, Migrator.scope), | ||
) | ||
|
||
old >= new -> NoopMigrationStrategy(false) | ||
else -> VersionRangeMigrationStrategy( | ||
versions = versions, | ||
strategy = DefaultMigrationStrategy(factory, migrationCompletedListener, Migrator.scope), | ||
) | ||
} | ||
return strategy | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +1,41 @@ | ||
package mihon.core.migration | ||
|
||
import kotlinx.coroutines.CompletableDeferred | ||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.Deferred | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.Job | ||
import kotlinx.coroutines.runBlocking | ||
import tachiyomi.core.common.util.system.logcat | ||
|
||
object Migrator { | ||
|
||
@SuppressWarnings("ReturnCount") | ||
fun migrate( | ||
private var result: Deferred<Boolean>? = null | ||
val scope = CoroutineScope(Dispatchers.Main + Job()) | ||
|
||
fun initialize( | ||
old: Int, | ||
new: Int, | ||
migrations: List<Migration>, | ||
dryrun: Boolean = false, | ||
onMigrationComplete: () -> Unit | ||
): Boolean { | ||
val migrationContext = MigrationContext() | ||
|
||
if (old == 0) { | ||
return migrationContext.migrate( | ||
migrations = migrations.filter { it.isAlways() }, | ||
dryrun = dryrun | ||
) | ||
.also { onMigrationComplete() } | ||
} | ||
|
||
if (old >= new) { | ||
return false | ||
} | ||
) { | ||
val migrationContext = MigrationContext(dryrun) | ||
val migrationJobFactory = MigrationJobFactory(migrationContext, scope) | ||
val migrationStrategyFactory = MigrationStrategyFactory(migrationJobFactory, onMigrationComplete) | ||
val strategy = migrationStrategyFactory.create(old, new) | ||
result = strategy(migrations) | ||
} | ||
|
||
return migrationContext.migrate( | ||
migrations = migrations.filter { it.isAlways() || it.version.toInt() in (old + 1)..new }, | ||
dryrun = dryrun | ||
) | ||
.also { onMigrationComplete() } | ||
suspend fun await(): Boolean { | ||
val result = result ?: CompletableDeferred(false) | ||
return result.await() | ||
} | ||
|
||
private fun Migration.isAlways() = version == Migration.ALWAYS | ||
fun release() { | ||
result = null | ||
} | ||
|
||
@SuppressWarnings("MaxLineLength") | ||
private fun MigrationContext.migrate(migrations: List<Migration>, dryrun: Boolean): Boolean { | ||
return migrations.sortedBy { it.version } | ||
.map { migration -> | ||
if (!dryrun) { | ||
logcat { "Running migration: { name = ${migration::class.simpleName}, version = ${migration.version} }" } | ||
runBlocking { migration(this@migrate) } | ||
} else { | ||
logcat { "(Dry-run) Running migration: { name = ${migration::class.simpleName}, version = ${migration.version} }" } | ||
true | ||
} | ||
} | ||
.reduce { acc, b -> acc || b } | ||
fun awaitAndRelease(): Boolean = runBlocking { | ||
await().also { release() } | ||
} | ||
} |
Oops, something went wrong.