Skip to content

Commit

Permalink
Add 'okay-fs' module
Browse files Browse the repository at this point in the history
  • Loading branch information
sellmair committed Apr 12, 2024
1 parent d2c1bac commit 4035280
Show file tree
Hide file tree
Showing 9 changed files with 291 additions and 4 deletions.
5 changes: 5 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
plugins {
kotlin("multiplatform") version "1.9.23" apply false
kotlin("jvm") version "1.9.23" apply false
kotlin("plugin.serialization") version "1.9.23" apply false
}
7 changes: 4 additions & 3 deletions core/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import kotlin.io.path.Path

plugins {
//kotlin("jvm") version "2.0.0-Beta5"
kotlin("jvm") version "1.9.23"
kotlin("plugin.serialization") version "1.9.23"
kotlin("jvm")
kotlin("plugin.serialization")
}

kotlin {
Expand Down Expand Up @@ -55,6 +54,8 @@ tasks.register<JavaExec>("buildTestProject") {
}

dependencies {
implementation(project(":okay-fs"))

implementation("io.ktor:ktor-client-cio:2.3.9")
implementation("io.ktor:ktor-client-core:2.3.9")
implementation("org.apache.maven:maven-model:3.9.6")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ internal object Base64Serializer : KSerializer<ByteArray> {
override fun serialize(encoder: Encoder, value: ByteArray) {
return encoder.encodeString(Base64.encode(value))
}
}
}
39 changes: 39 additions & 0 deletions okay-fs/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeTest
import org.jetbrains.kotlin.gradle.tasks.KotlinTest

plugins {
kotlin("multiplatform")
kotlin("plugin.serialization")
}

kotlin {
jvm()
macosX64()
macosArm64()
linuxArm64()
linuxX64()

sourceSets.commonMain.dependencies {
implementation("com.squareup.okio:okio:3.9.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.3")
}

sourceSets.commonTest.dependencies {
implementation(kotlin("test"))
implementation("com.squareup.okio:okio-fakefilesystem:3.9.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
}
}

tasks.withType<KotlinNativeTest>().configureEach {
testLogging {
this.showStandardStreams = true
this.showCauses = true
this.showExceptions = true
this.showStackTraces = true
this.exceptionFormat = TestExceptionFormat.FULL
}


}
92 changes: 92 additions & 0 deletions okay-fs/src/commonMain/kotlin/io/sellmair/okay/fs/OkFs.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package io.sellmair.okay.fs

import okio.FileSystem
import okio.Path
import okio.Path.Companion.toPath
import okio.SYSTEM

fun OkFs(root: String): OkFs {
return OkFsImpl(root.toPath())
}

sealed interface OkFs {

fun path(path: String): OkPath = OkPath(path.toPath(), this)

fun exists(path: OkPath): Boolean

fun isRegularFile(path: OkPath): Boolean

fun isDirectory(path: OkPath): Boolean

fun absolutePath(path: OkPath): String

fun createDirectories(path: OkPath)

fun delete(path: OkPath)

fun deleteRecursively(path: OkPath)

fun write(path: OkPath, data: ByteArray)

}

internal val OkFs.impl get() = when(this) {
is OkFsImpl -> this
}

internal class OkFsImpl(
val root: Path, private val delegate: FileSystem = FileSystem.SYSTEM
): OkFs {

private val OkPath.resolved: Path
get() = root.resolve(relative)

override fun exists(path: OkPath): Boolean {
return delegate.exists(path.resolved)
}

override fun isRegularFile(path: OkPath): Boolean {
return delegate.metadataOrNull(path.resolved)?.isRegularFile == true
}

override fun isDirectory(path: OkPath): Boolean {
return delegate.metadataOrNull(path.resolved)?.isDirectory == true
}

override fun absolutePath(path: OkPath): String {
return path.resolved.toString()
}

override fun createDirectories(path: OkPath) {
delegate.createDirectories(path.resolved, mustCreate = true)
}

override fun delete(path: OkPath) {
delegate.delete(path.resolved, mustExist = false)
}

override fun deleteRecursively(path: OkPath) {
delegate.deleteRecursively(path.resolved, mustExist = false)
}

override fun write(path: OkPath, data: ByteArray) {
delegate.write(path.resolved, mustCreate = false) {
this.write(data)
}
}

override fun equals(other: Any?): Boolean {
if(this === other) return true
if(other !is OkFsImpl) return false
if(this.root != other.root) return false
if(delegate::class != other.delegate::class) return false
return true
}

override fun hashCode(): Int {
var hash = root.hashCode()
hash = 31 * hash + delegate::class.hashCode()
return hash
}
}
45 changes: 45 additions & 0 deletions okay-fs/src/commonMain/kotlin/io/sellmair/okay/fs/OkPath.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package io.sellmair.okay.fs

import kotlinx.serialization.Serializable
import okio.Path

@Serializable(OkPathSerializer::class)
class OkPath internal constructor(
internal val relative: Path,
internal val fs: OkFs
) {
init {
require(relative.isRelative) { "Expected '$relative' to be a relative path" }
}

val parent: OkPath?
get() = relative.parent?.let { parentPath -> OkPath(parentPath, fs) }

override fun toString(): String {
return relative.toString()
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is OkPath) return false
if (this.relative != other.relative) return false
if (this.fs != other.fs) return false
return true
}

override fun hashCode(): Int {
var hash = relative.hashCode()
hash = hash * 31 + fs.hashCode()
return hash
}
}

fun OkPath.isRegularFile(): Boolean = fs.isRegularFile(this)

fun OkPath.isDirectory(): Boolean = fs.isDirectory(this)

fun OkPath.createDirectories() = fs.createDirectories(this)

fun OkPath.delete() = fs.delete(this)

fun OkPath.write(data: ByteArray) = fs.write(this, data)
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package io.sellmair.okay.fs

import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.descriptors.element
import kotlinx.serialization.encoding.*
import okio.Path.Companion.toPath


internal object OkPathSerializer : KSerializer<OkPath> {
override val descriptor = buildClassSerialDescriptor("OkPath") {
element<String>("root")
element<String>("path")
}

override fun deserialize(decoder: Decoder): OkPath {
return decoder.decodeStructure(descriptor) {
var root: String? = null
var path: String? = null

while(true) {
when(val index = decodeElementIndex(descriptor)) {
0 -> root = decodeStringElement(descriptor, index)
1 -> path = decodeStringElement(descriptor, index)
CompositeDecoder.DECODE_DONE -> break
}
}

OkPath(
relative = (path ?: error("Missing 'path'")).toPath(),
fs = OkFsImpl((root ?: error("Missing 'root'")).toPath())
)
}
}

override fun serialize(encoder: Encoder, value: OkPath) {
encoder.encodeStructure(descriptor) {
encodeStringElement(descriptor, 0, value.fs.impl.root.toString())
encodeStringElement(descriptor, 1, value.relative.toString())
}
}
}
56 changes: 56 additions & 0 deletions okay-fs/src/commonTest/kotlin/io/sellmair/okay/fs/OkPathTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package io.sellmair.okay.fs

import kotlinx.serialization.json.Json
import okio.Path.Companion.toPath
import okio.fakefilesystem.FakeFileSystem
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue

class OkPathTest {

@Test
fun `test - isRegularFile`() {
val fs = OkFsImpl("my/workingDir".toPath(), FakeFileSystem())
val path = fs.path("foo")

// File does not exist at all!
assertFalse(path.isRegularFile(), "File is not present!")

// Create as directory
path.createDirectories()
assertFalse(path.isRegularFile(), "File is a directory")
assertTrue(path.isDirectory(), "File is a directory")

// Delete and create as file
path.delete()
path.write("hello".encodeToByteArray())
assertTrue(path.isRegularFile(), "File is regular file")

// Delete again
path.delete()
assertFalse(path.isRegularFile(), "File was deleted")
}

@Test
fun `test - serialization`() {
val fs = OkFs("/my/working/dir")
val path = fs.path("my/path")

val format = Json { prettyPrint = true }

val json = format.encodeToString(OkPath.serializer(), path)
assertEquals(
"""
{
"root": "/my/working/dir",
"path": "my/path"
}""".trimIndent(),
json
)

val decoded = format.decodeFromString<OkPath>(json)
assertEquals(path, decoded)
}
}
7 changes: 7 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
rootProject.name = "okay"

include(":core")
include(":okay-fs")

dependencyResolutionManagement {
repositories {
mavenCentral()
}
}

0 comments on commit 4035280

Please sign in to comment.