-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #14 from ReneeVandervelde/data
Add Data module for storage abstractions
- Loading branch information
Showing
49 changed files
with
2,551 additions
and
6 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
plugins { | ||
id("android-library") | ||
id("com.android.library") | ||
kotlin("multiplatform") | ||
} | ||
|
||
|
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
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
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,72 @@ | ||
plugins { | ||
id("multiplatform.tier1") | ||
id("multiplatform.android") | ||
id("app.cash.sqldelight") | ||
id("ink.publishing") | ||
} | ||
|
||
android { | ||
namespace = "com.inkapplications.regolith.data" | ||
} | ||
|
||
|
||
sqldelight { | ||
databases { | ||
create("Settings") { | ||
packageName.set("regolith.data") | ||
schemaOutputDirectory.set(file("src/commonMain/sqldelight/databases")) | ||
generateAsync = true | ||
} | ||
} | ||
} | ||
|
||
kotlin { | ||
androidTarget { | ||
publishAllLibraryVariants() | ||
} | ||
|
||
sourceSets { | ||
val commonMain by getting { | ||
dependencies { | ||
implementation(libs.coroutines.core) | ||
implementation(libs.sqldelight.coroutines) | ||
api(libs.watermelon.data) | ||
} | ||
} | ||
|
||
val jvmMain by getting { | ||
dependencies { | ||
implementation(libs.sqldelight.jvm) | ||
} | ||
} | ||
|
||
val androidMain by getting { | ||
dependencies { | ||
implementation(libs.sqldelight.android) | ||
} | ||
} | ||
|
||
val nativeMain by getting { | ||
dependencies { | ||
implementation(libs.sqldelight.native) | ||
} | ||
} | ||
|
||
val jsMain by getting { | ||
dependencies { | ||
implementation(libs.sqldelight.web.worker.driver) | ||
implementation(devNpm("copy-webpack-plugin", "9.1.0")) | ||
implementation(npm("sql.js", "1.6.2")) | ||
implementation(devNpm("copy-webpack-plugin", "9.1.0")) | ||
} | ||
} | ||
|
||
val commonTest by getting { | ||
dependencies { | ||
implementation(libs.kotlin.test.core) | ||
implementation(libs.kotlin.test.junit) | ||
implementation(libs.coroutines.test) | ||
} | ||
} | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
data/src/androidMain/kotlin/regolith/data/settings/AndroidSettingsModule.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 regolith.data.settings | ||
|
||
import android.content.Context | ||
import app.cash.sqldelight.async.coroutines.synchronous | ||
import app.cash.sqldelight.driver.android.AndroidSqliteDriver | ||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.Dispatchers | ||
import regolith.data.Settings | ||
|
||
/** | ||
* Main entry-point for creating settings services on Android. | ||
*/ | ||
class AndroidSettingsModule( | ||
context: Context, | ||
migrationScope: CoroutineScope = CoroutineScope(Dispatchers.IO) | ||
) { | ||
private val driver = AndroidSqliteDriver(Settings.Schema.synchronous(), context, "settings.db") | ||
|
||
/** | ||
* Read/write access to the settings database. | ||
*/ | ||
val settingsAccess = createDatabaseAccess(migrationScope, driver) | ||
} |
26 changes: 26 additions & 0 deletions
26
data/src/commonMain/kotlin/regolith/data/settings/Described.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,26 @@ | ||
package regolith.data.settings | ||
|
||
/** | ||
* Fields to allow a setting to be displayed in a UI | ||
*/ | ||
interface Described { | ||
/** | ||
* A user-readable name for the setting. | ||
*/ | ||
val name: String | ||
|
||
/** | ||
* Visibility level of the setting. Used to show/hide advanced settings. | ||
*/ | ||
val level: SettingLevel | ||
|
||
/** | ||
* An optional user-readable category name that can be used for grouping. | ||
*/ | ||
val category: SettingCategory? | ||
|
||
/** | ||
* An optional user-readable description of the setting. | ||
*/ | ||
val description: String? | ||
} |
13 changes: 13 additions & 0 deletions
13
data/src/commonMain/kotlin/regolith/data/settings/Keyed.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,13 @@ | ||
package regolith.data.settings | ||
|
||
/** | ||
* Object that identifies the key in a key/value pair. | ||
*/ | ||
interface Keyed { | ||
/** | ||
* A unique key that identifies the setting. | ||
* | ||
* This key should never change in order to preserve data. | ||
*/ | ||
val key: String | ||
} |
15 changes: 15 additions & 0 deletions
15
data/src/commonMain/kotlin/regolith/data/settings/SettingCategory.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,15 @@ | ||
package regolith.data.settings | ||
|
||
/** | ||
* A named group of settings. | ||
* | ||
* The [key] and [name] can be the same, provided that the name is | ||
* unique per category. This can be useful if the name will be generated | ||
* from localization tools. | ||
*/ | ||
data class SettingCategory( | ||
val key: String, | ||
val name: String, | ||
) { | ||
constructor(key: String): this(key, key) | ||
} |
36 changes: 36 additions & 0 deletions
36
data/src/commonMain/kotlin/regolith/data/settings/SettingLevel.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,36 @@ | ||
package regolith.data.settings | ||
|
||
/** | ||
* Visibility of the setting. | ||
* | ||
* This value can be used to control which settings are available in | ||
* a settings panel. | ||
*/ | ||
enum class SettingLevel { | ||
/** | ||
* Setting is visible to any user in the system. | ||
*/ | ||
User, | ||
|
||
/** | ||
* Setting is visible to users, but in an 'advanced' section or flagged | ||
* appropriately. | ||
*/ | ||
Advanced, | ||
|
||
/** | ||
* Setting should only be hidden from normal users, but visible for | ||
* development and debugging. | ||
*/ | ||
Dev, | ||
|
||
/** | ||
* Setting should be hidden entirely from the user interface and only | ||
* controlled programmatically. | ||
*/ | ||
Hidden; | ||
|
||
companion object { | ||
val DEFAULT = User | ||
} | ||
} |
47 changes: 47 additions & 0 deletions
47
data/src/commonMain/kotlin/regolith/data/settings/SettingsAccess.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,47 @@ | ||
package regolith.data.settings | ||
|
||
import kotlinx.coroutines.flow.Flow | ||
import kotlinx.coroutines.flow.map | ||
import regolith.data.settings.structure.DataSetting | ||
import regolith.data.settings.structure.PrimitiveSetting | ||
|
||
/** | ||
* Provides read/write access to settings. | ||
*/ | ||
interface SettingsAccess { | ||
/** | ||
* Observe changes to a setting. | ||
*/ | ||
fun <STORED> observeSetting(setting: PrimitiveSetting<STORED>): Flow<STORED> | ||
|
||
/** | ||
* Get the current value of a setting. | ||
*/ | ||
suspend fun <STORED> getSetting(setting: PrimitiveSetting<STORED>): STORED | ||
|
||
/** | ||
* Write a new value to a setting. | ||
*/ | ||
suspend fun <STORED> writeSetting(setting: PrimitiveSetting<STORED>, value: STORED) | ||
} | ||
|
||
/** | ||
* Get the current value of a data setting. | ||
*/ | ||
suspend fun <STORED, DATA> SettingsAccess.getSetting(setting: DataSetting<STORED, DATA>): DATA { | ||
return getSetting(setting.toPrimitive()).let { setting.dataTransformer.transform(it) } | ||
} | ||
|
||
/** | ||
* Write a new value to a data setting. | ||
*/ | ||
suspend fun <STORED, DATA> SettingsAccess.writeSetting(setting: DataSetting<STORED, DATA>, value: DATA) { | ||
writeSetting(setting.toPrimitive(), setting.dataTransformer.reverseTransform(value)) | ||
} | ||
|
||
/** | ||
* Observe changes to a data setting. | ||
*/ | ||
fun <STORED, DATA> SettingsAccess.observeSetting(setting: DataSetting<STORED, DATA>): Flow<DATA> { | ||
return observeSetting(setting.toPrimitive()).map { setting.dataTransformer.transform(it) } | ||
} |
82 changes: 82 additions & 0 deletions
82
data/src/commonMain/kotlin/regolith/data/settings/SettingsDatabaseAccess.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,82 @@ | ||
package regolith.data.settings | ||
|
||
import app.cash.sqldelight.coroutines.asFlow | ||
import app.cash.sqldelight.db.SqlDriver | ||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.Deferred | ||
import kotlinx.coroutines.async | ||
import kotlinx.coroutines.flow.Flow | ||
import kotlinx.coroutines.flow.flatMapLatest | ||
import kotlinx.coroutines.flow.flow | ||
import kotlinx.coroutines.flow.map | ||
import regolith.data.Settings | ||
import regolith.data.settings.structure.* | ||
|
||
internal class SettingsDatabaseAccess( | ||
private val database: Deferred<Settings>, | ||
): SettingsAccess { | ||
override fun <STORED> observeSetting(setting: PrimitiveSetting<STORED>): Flow<STORED> { | ||
val queries = flow { emit(database.await().settingsQueries) } | ||
return when (setting) { | ||
is StringSetting -> queries.flatMapLatest { it.getStringValue(setting.key).asFlow() } | ||
.map { it.executeAsOneOrNull() } | ||
.map { it?.stringValue } | ||
is LongSetting -> queries.flatMapLatest { it.getIntValue(setting.key).asFlow() } | ||
.map { it.executeAsOneOrNull() } | ||
.map { it?.intValue } | ||
is DoubleSetting -> queries.flatMapLatest { it.getRealValue(setting.key).asFlow() } | ||
.map { it.executeAsOneOrNull() } | ||
.map { it?.realValue } | ||
is BlobSetting -> queries.flatMapLatest { it.getBlobValue(setting.key).asFlow() } | ||
.map { it.executeAsOneOrNull() } | ||
.map { it?.blobValue } | ||
}.map { it as STORED } | ||
} | ||
|
||
override suspend fun <STORED> getSetting(setting: PrimitiveSetting<STORED>): STORED { | ||
return when (setting) { | ||
is StringSetting -> database.await().settingsQueries.getStringValue(setting.key) | ||
.executeAsOneOrNull() | ||
?.stringValue | ||
is LongSetting -> database.await().settingsQueries.getIntValue(setting.key) | ||
.executeAsOneOrNull() | ||
?.intValue | ||
is DoubleSetting -> database.await().settingsQueries.getRealValue(setting.key) | ||
.executeAsOneOrNull() | ||
?.realValue | ||
is BlobSetting -> database.await().settingsQueries.getBlobValue(setting.key) | ||
.executeAsOneOrNull() | ||
?.blobValue | ||
} as STORED | ||
} | ||
|
||
override suspend fun <STORED> writeSetting(setting: PrimitiveSetting<STORED>, value: STORED) { | ||
when (setting) { | ||
is StringSetting -> database.await().settingsQueries.setStringValue( | ||
key = setting.key, | ||
stringValue = value as String?, | ||
) | ||
is LongSetting -> database.await().settingsQueries.setIntValue( | ||
key = setting.key, | ||
intValue = value as Long?, | ||
) | ||
is DoubleSetting -> database.await().settingsQueries.setRealValue( | ||
key = setting.key, | ||
realValue = value as Double?, | ||
) | ||
is BlobSetting -> database.await().settingsQueries.setBlobValue( | ||
key = setting.key, | ||
blobValue = value as ByteArray?, | ||
) | ||
} | ||
} | ||
} | ||
|
||
internal fun createDatabaseAccess(scope: CoroutineScope, driver: SqlDriver): SettingsAccess { | ||
val database = scope.async { | ||
Settings.Schema.create(driver).await() | ||
Settings(driver) | ||
} | ||
|
||
return SettingsDatabaseAccess(database) | ||
} |
13 changes: 13 additions & 0 deletions
13
data/src/commonMain/kotlin/regolith/data/settings/Validated.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,13 @@ | ||
package regolith.data.settings | ||
|
||
import com.inkapplications.data.validator.DataValidator | ||
|
||
/** | ||
* Constructs used for data validation | ||
*/ | ||
interface Validated<DATA> { | ||
/** | ||
* Validator to be used on user-supplied input fields before storage. | ||
*/ | ||
val inputValidator: DataValidator<DATA> | ||
} |
Oops, something went wrong.