Skip to content

Commit

Permalink
Add interceptors to filesystem (#206)
Browse files Browse the repository at this point in the history
  • Loading branch information
illarionov authored Aug 26, 2024
1 parent e6e77b1 commit 0b6d63b
Show file tree
Hide file tree
Showing 91 changed files with 858 additions and 457 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import arrow.core.left
import ru.pixnews.wasm.sqlite.open.helper.common.api.Logger
import ru.pixnews.wasm.sqlite.open.helper.host.base.memory.DefaultWasiMemoryReader
import ru.pixnews.wasm.sqlite.open.helper.host.base.memory.WasiMemoryReader
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.FileSystem
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.error.BadFileDescriptor
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.error.ReadError
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.ext.readCatching
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.model.Fd
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.nio.JvmNioFileSystem
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.nio.op.RunWithChannelFd
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.op.readwrite.ReadWriteStrategy
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.op.readwrite.ReadWriteStrategy.CHANGE_POSITION
Expand All @@ -26,7 +26,7 @@ import java.nio.channels.FileChannel

internal class GraalInputStreamWasiMemoryReader(
private val memory: GraalvmWasmHostMemoryAdapter,
private val fileSystem: JvmNioFileSystem,
private val fileSystem: FileSystem,
logger: Logger,
) : WasiMemoryReader {
private val wasmMemory get() = memory.wasmMemory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import ru.pixnews.wasm.sqlite.open.helper.host.EmbedderHost
import ru.pixnews.wasm.sqlite.open.helper.host.base.WasmPtr
import ru.pixnews.wasm.sqlite.open.helper.host.base.memory.WasiMemoryReader
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.model.Fd
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.nio.JvmNioFileSystem
import ru.pixnews.wasm.sqlite.open.helper.host.wasi.preview1.function.FdReadFdPreadFunctionHandle
import ru.pixnews.wasm.sqlite.open.helper.host.wasi.preview1.type.Iovec

Expand Down Expand Up @@ -73,7 +72,7 @@ private class FdRead(
val hostMemory = memory.toHostMemory()
val wasiMemoryReader: WasiMemoryReader = GraalInputStreamWasiMemoryReader(
hostMemory,
handle.host.fileSystem as? JvmNioFileSystem ?: error("JvmFileSystem expected"),
handle.host.fileSystem,
this.handle.host.rootLogger,
)
return handle.execute(hostMemory, wasiMemoryReader, Fd(fd), pIov, iovCnt, pNum).code
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import ru.pixnews.wasm.sqlite.driver.test.base.tests.AbstractSqliteDriverTest
import ru.pixnews.wasm.sqlite.driver.test.base.tests.TestSqliteDriverFactory
import ru.pixnews.wasm.sqlite.driver.test.base.tests.room.UserDatabaseTests.UserDatabaseFactory
import kotlin.test.Test
import kotlin.time.Duration.Companion.minutes

public abstract class AbstractBasicRoomTest<S>(
driverFactory: TestSqliteDriverFactory<S>,
Expand All @@ -25,7 +26,7 @@ public abstract class AbstractBasicRoomTest<S>(
val tests = UserDatabaseTests(driverFactory, databaseFactory, logger, dbLogger)

@Test
open fun Test_Room() = runTest {
open fun Test_Room() = runTest(timeout = 3.minutes) {
tests.testRoomOnUserDatabase(
databaseName = fileInTempDir("test.db"),
queryCoroutineContext = coroutineContext,
Expand All @@ -34,7 +35,7 @@ public abstract class AbstractBasicRoomTest<S>(
}

@Test
open fun Test_In_Memory_Room() = runTest {
open fun Test_In_Memory_Room() = runTest(timeout = 3.minutes) {
tests.testRoomOnUserDatabase(
databaseName = null,
queryCoroutineContext = this.coroutineContext,
Expand Down
123 changes: 78 additions & 45 deletions wasi-emscripten-fs/api/wasi-emscripten-fs.api

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions wasi-emscripten-fs/src/commonMain/kotlin/FileSystem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,26 @@
package ru.pixnews.wasm.sqlite.open.helper.host.filesystem

import arrow.core.Either
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.dsl.FileSystemCommonConfig
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.dsl.FileSystemConfigBlock
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.dsl.FileSystemEngineConfig
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.error.FileSystemOperationError
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.op.FileSystemOperation

public fun <E : FileSystemEngineConfig> FileSystem(
engine: FileSystemEngine<E>,
block: FileSystemConfigBlock<E>.() -> Unit = {},
): FileSystem {
val config = FileSystemConfigBlock<E>().apply(block)
val commonConfig = object : FileSystemCommonConfig {
override val interceptors: List<FileSystemInterceptor> = config.interceptors
}
return engine.create(
commonConfig = commonConfig,
engineConfig = config.engineConfig,
)
}

public interface FileSystem : AutoCloseable {
public fun <I : Any, E : FileSystemOperationError, R : Any> execute(
operation: FileSystemOperation<I, E, R>,
Expand Down
19 changes: 19 additions & 0 deletions wasi-emscripten-fs/src/commonMain/kotlin/FileSystemEngine.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright 2024, the wasm-sqlite-open-helper project authors and contributors. Please see the AUTHORS file
* for details. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* SPDX-License-Identifier: Apache-2.0
*/

package ru.pixnews.wasm.sqlite.open.helper.host.filesystem

import ru.pixnews.wasm.sqlite.open.helper.common.api.InternalWasmSqliteHelperApi
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.dsl.FileSystemCommonConfig
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.dsl.FileSystemEngineConfig

public interface FileSystemEngine<E : FileSystemEngineConfig> {
@InternalWasmSqliteHelperApi
public fun create(
commonConfig: FileSystemCommonConfig,
engineConfig: E.() -> Unit,
): FileSystem
}
26 changes: 26 additions & 0 deletions wasi-emscripten-fs/src/commonMain/kotlin/FileSystemInterceptor.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2024, the wasm-sqlite-open-helper project authors and contributors. Please see the AUTHORS file
* for details. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* SPDX-License-Identifier: Apache-2.0
*/

@file:Suppress("WRONG_MULTIPLE_MODIFIERS_ORDER")

package ru.pixnews.wasm.sqlite.open.helper.host.filesystem

import arrow.core.Either
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.error.FileSystemOperationError
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.op.FileSystemOperation

public interface FileSystemInterceptor {
public fun <I : Any, E : FileSystemOperationError, R : Any> intercept(
chain: Chain<I, E, R>,
): Either<E, R>

public interface Chain<I : Any, out E : FileSystemOperationError, out R : Any> {
public val operation: FileSystemOperation<I, E, R>
public val input: I

public fun proceed(input: I): Either<E, R>
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright 2024, the wasm-sqlite-open-helper project authors and contributors. Please see the AUTHORS file
* for details. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* SPDX-License-Identifier: Apache-2.0
*/

package ru.pixnews.wasm.sqlite.open.helper.host.filesystem.dsl

import ru.pixnews.wasm.sqlite.open.helper.common.api.InternalWasmSqliteHelperApi
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.FileSystemInterceptor

@InternalWasmSqliteHelperApi
public interface FileSystemCommonConfig {
public val interceptors: List<FileSystemInterceptor>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2024, the wasm-sqlite-open-helper project authors and contributors. Please see the AUTHORS file
* for details. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* SPDX-License-Identifier: Apache-2.0
*/

package ru.pixnews.wasm.sqlite.open.helper.host.filesystem.dsl

import arrow.core.Either
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.FileSystemInterceptor
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.FileSystemInterceptor.Chain
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.error.FileSystemOperationError

@FileSystemDsl
public class FileSystemConfigBlock<E : FileSystemEngineConfig> {
private val _interceptors: MutableList<FileSystemInterceptor> = mutableListOf()
internal val interceptors: List<FileSystemInterceptor> get() = _interceptors

internal var engineConfig: E.() -> Unit = {}
private set

public fun addInterceptor(interceptor: FileSystemInterceptor) {
_interceptors += interceptor
}

public inline fun addInterceptor(
crossinline block: (chain: Chain<Any, FileSystemOperationError, Any>) -> Either<FileSystemOperationError, *>,
): Unit = addInterceptor(
object : FileSystemInterceptor {
override fun <I : Any, E : FileSystemOperationError, R : Any> intercept(
chain: Chain<I, E, R>,
): Either<E, R> {
@Suppress("UNCHECKED_CAST")
return block(chain as Chain<Any, FileSystemOperationError, Any>) as Either<E, R>
}
},
)

public fun engine(block: E.() -> Unit) {
val oldConfig = engineConfig
engineConfig = {
oldConfig()
block()
}
}
}
11 changes: 11 additions & 0 deletions wasi-emscripten-fs/src/commonMain/kotlin/dsl/FileSystemDsl.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Copyright 2024, the wasm-sqlite-open-helper project authors and contributors. Please see the AUTHORS file
* for details. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* SPDX-License-Identifier: Apache-2.0
*/

package ru.pixnews.wasm.sqlite.open.helper.host.filesystem.dsl

@DslMarker
@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPEALIAS, AnnotationTarget.TYPE, AnnotationTarget.FUNCTION)
public annotation class FileSystemDsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright 2024, the wasm-sqlite-open-helper project authors and contributors. Please see the AUTHORS file
* for details. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* SPDX-License-Identifier: Apache-2.0
*/

package ru.pixnews.wasm.sqlite.open.helper.host.filesystem.dsl

@FileSystemDsl
public interface FileSystemEngineConfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2024, the wasm-sqlite-open-helper project authors and contributors. Please see the AUTHORS file
* for details. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* SPDX-License-Identifier: Apache-2.0
*/

package ru.pixnews.wasm.sqlite.open.helper.host.filesystem.internal.delegatefs

import arrow.core.Either
import arrow.core.left
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.FileSystem
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.FileSystemInterceptor
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.FileSystemInterceptor.Chain
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.error.FileSystemOperationError
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.error.NotImplemented
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.op.FileSystemOperation

internal class DelegateOperationsFileSystem(
private val operations: Map<FileSystemOperation<*, *, *>, FileSystemOperationHandler<*, *, *>>,
interceptors: List<FileSystemInterceptor>,
) : FileSystem {
private val interceptors: List<FileSystemInterceptor> = buildList {
addAll(interceptors)
add(ExecuteOperationInterceptor(operations))
}

override fun <I : Any, E : FileSystemOperationError, R : Any> execute(
operation: FileSystemOperation<I, E, R>,
input: I,
): Either<E, R> {
val chain = InterceptorChain(
operation = operation,
input = input,
interceptors = interceptors,
)
return chain.proceed(input)
}

override fun isOperationSupported(operation: FileSystemOperation<*, *, *>): Boolean {
return operations.containsKey(operation)
}

override fun close() = Unit

private class ExecuteOperationInterceptor(
private val operations: Map<FileSystemOperation<*, *, *>, FileSystemOperationHandler<*, *, *>>,
) : FileSystemInterceptor {
@Suppress("UNCHECKED_CAST")
override fun <I : Any, E : FileSystemOperationError, R : Any> intercept(chain: Chain<I, E, R>): Either<E, R> {
val handler = operations[chain.operation] as? FileSystemOperationHandler<I, E, R>
if (handler == null) {
return NotImplemented.left() as Either<E, Nothing>
}

return handler.invoke(chain.input)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
* SPDX-License-Identifier: Apache-2.0
*/

package ru.pixnews.wasm.sqlite.open.helper.host.filesystem.posix.base
package ru.pixnews.wasm.sqlite.open.helper.host.filesystem.internal.delegatefs

import arrow.core.Either
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.error.FileSystemOperationError

internal fun interface PosixOperationHandler<
internal fun interface FileSystemOperationHandler<
in I : Any,
out E : FileSystemOperationError,
out R : Any,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2024, the wasm-sqlite-open-helper project authors and contributors. Please see the AUTHORS file
* for details. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* SPDX-License-Identifier: Apache-2.0
*/

package ru.pixnews.wasm.sqlite.open.helper.host.filesystem.internal.delegatefs

import arrow.core.Either
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.FileSystemInterceptor
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.error.FileSystemOperationError
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.op.FileSystemOperation

internal class InterceptorChain<I : Any, out E : FileSystemOperationError, out R : Any>(
override val operation: FileSystemOperation<I, E, R>,
override val input: I,
private val interceptors: List<FileSystemInterceptor>,
private val index: Int = 0,
) : FileSystemInterceptor.Chain<I, E, R> {
override fun proceed(input: I): Either<E, R> {
val interceptor = interceptors.getOrNull(index) ?: error("End of interceptor chain")
val next = InterceptorChain(
interceptors = interceptors,
index = index + 1,
operation = operation,
input = input,
)
return interceptor.intercept(next)
}
}
Loading

0 comments on commit 0b6d63b

Please sign in to comment.