Initial Commit

This commit is contained in:
2023-02-15 23:03:46 -08:00
commit b04dadbeb1
25 changed files with 851 additions and 0 deletions

View File

@ -0,0 +1,3 @@
plugins {
dough_component
}

View File

@ -0,0 +1,40 @@
package gay.pizza.dough.fs
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerializationStrategy
interface FsOperations {
fun exists(path: FsPath): Boolean
fun isDirectory(path: FsPath): Boolean
fun isRegularFile(path: FsPath): Boolean
fun isSymbolicLink(path: FsPath): Boolean
fun isReadable(path: FsPath): Boolean
fun isWritable(path: FsPath): Boolean
fun isExecutable(path: FsPath): Boolean
fun list(path: FsPath): Sequence<FsPath>
fun walk(path: FsPath): Sequence<FsPath>
fun visit(path: FsPath, visitor: FsPathVisitor)
fun readString(path: FsPath): String
fun readAllBytes(path: FsPath): ByteArray
fun readBytesChunked(path: FsPath, block: (ByteArray, Int) -> Unit)
fun <T> readJsonFile(path: FsPath, deserializer: DeserializationStrategy<T>): T
fun readLines(path: FsPath, block: (String) -> Unit)
fun readLines(path: FsPath): Sequence<String>
fun <T> readJsonLinesToList(path: FsPath, deserializer: DeserializationStrategy<T>, block: (T) -> Unit)
fun <T> readJsonLinesToList(path: FsPath, deserializer: DeserializationStrategy<T>): List<T>
fun writeString(path: FsPath, content: String)
fun writeAllBytes(path: FsPath, bytes: ByteArray)
fun <T> writeJsonFile(path: FsPath, serializer: SerializationStrategy<T>, value: T)
fun delete(path: FsPath)
fun deleteOnExit(path: FsPath)
fun deleteRecursively(path: FsPath)
fun createDirectories(path: FsPath)
}

View File

@ -0,0 +1,19 @@
package gay.pizza.dough.fs
import kotlinx.serialization.Serializable
@Serializable(with = FsPathSerializer::class)
interface FsPath : Comparable<FsPath> {
val fullPathString: String
val entityNameString: String
val parent: FsPath?
val operations: FsOperations
fun resolve(part: String): FsPath
fun relativeTo(path: FsPath): FsPath
override fun compareTo(other: FsPath): Int {
return fullPathString.compareTo(other.fullPathString)
}
}

View File

@ -0,0 +1,19 @@
package gay.pizza.dough.fs
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
object FsPathSerializer : KSerializer<FsPath> {
override val descriptor: SerialDescriptor = String.serializer().descriptor
override fun deserialize(decoder: Decoder): FsPath {
return FsPath(decoder.decodeString())
}
override fun serialize(encoder: Encoder, value: FsPath) {
encoder.encodeString(value.fullPathString)
}
}

View File

@ -0,0 +1,15 @@
package gay.pizza.dough.fs
interface FsPathVisitor {
fun beforeVisitDirectory(path: FsPath): VisitResult
fun visitFile(path: FsPath): VisitResult
fun visitFileFailed(path: FsPath, exception: Exception): VisitResult
fun afterVisitDirectory(path: FsPath): VisitResult
enum class VisitResult {
Continue,
Terminate,
SkipSubtree,
SkipSiblings
}
}

View File

@ -0,0 +1,85 @@
package gay.pizza.dough.fs
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerializationStrategy
fun FsPath.exists(): Boolean =
operations.exists(this)
fun FsPath.isDirectory(): Boolean =
operations.isDirectory(this)
fun FsPath.isRegularFile(): Boolean =
operations.isRegularFile(this)
fun FsPath.isSymbolicLink(): Boolean =
operations.isSymbolicLink(this)
fun FsPath.isReadable(): Boolean =
operations.isReadable(this)
fun FsPath.isWritable(): Boolean =
operations.isWritable(this)
fun FsPath.isExecutable(): Boolean =
operations.isExecutable(this)
fun FsPath.list(): Sequence<FsPath> =
operations.list(this)
fun FsPath.walk(): Sequence<FsPath> =
operations.walk(this)
fun FsPath.visit(visitor: FsPathVisitor): Unit =
operations.visit(this, visitor)
fun FsPath.readString(): String =
operations.readString(this)
fun FsPath.readAllBytes(): ByteArray =
operations.readAllBytes(this)
fun FsPath.readBytesChunked(block: (ByteArray, Int) -> Unit) =
operations.readBytesChunked(this, block)
fun <T> FsPath.readJsonFile(deserializer: DeserializationStrategy<T>): T =
operations.readJsonFile(this, deserializer)
fun FsPath.readLines(block: (String) -> Unit): Unit =
operations.readLines(this, block)
fun FsPath.readLines(): Sequence<String> =
operations.readLines(this)
fun FsPath.readLinesToList(): List<String> {
val lines = mutableListOf<String>()
readLines { line -> lines.add(line) }
return lines
}
fun <T> FsPath.readJsonLinesToList(deserializer: DeserializationStrategy<T>, block: (T) -> Unit) =
operations.readJsonLinesToList(this, deserializer, block)
fun <T> FsPath.readJsonLinesToList(deserializer: DeserializationStrategy<T>): List<T> =
operations.readJsonLinesToList(this, deserializer)
fun FsPath.writeString(content: String): Unit =
operations.writeString(this, content)
fun FsPath.writeAllBytes(bytes: ByteArray): Unit =
operations.writeAllBytes(this, bytes)
fun <T> FsPath.writeJsonFile(serializer: SerializationStrategy<T>, value: T): Unit =
operations.writeJsonFile(this, serializer, value)
fun FsPath.delete(): Unit =
operations.delete(this)
fun FsPath.deleteOnExit(): Unit =
operations.delete(this)
fun FsPath.deleteRecursively(): Unit =
operations.deleteRecursively(this)
fun FsPath.createDirectories(): Unit =
operations.createDirectories(this)

View File

@ -0,0 +1,3 @@
package gay.pizza.dough.fs
expect fun FsPath(path: String): FsPath

View File

@ -0,0 +1,12 @@
package gay.pizza.dough.fs
import java.nio.file.Path
fun Path.toFsPath(): FsPath = JavaPath(this)
fun FsPath.toJavaPath(): Path {
return if (this is JavaPath) {
javaPath
} else {
Path.of(fullPathString)
}
}

View File

@ -0,0 +1,87 @@
package gay.pizza.dough.fs
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.json.Json
import java.nio.file.Files
import kotlin.io.path.bufferedReader
import kotlin.io.path.deleteExisting
import kotlin.io.path.inputStream
import kotlin.streams.asSequence
object JavaFsOperations : FsOperations {
override fun exists(path: FsPath): Boolean = Files.exists(path.toJavaPath())
override fun isDirectory(path: FsPath): Boolean = Files.isDirectory(path.toJavaPath())
override fun isRegularFile(path: FsPath): Boolean = Files.isRegularFile(path.toJavaPath())
override fun isSymbolicLink(path: FsPath): Boolean = Files.isSymbolicLink(path.toJavaPath())
override fun isReadable(path: FsPath): Boolean = Files.isReadable(path.toJavaPath())
override fun isWritable(path: FsPath): Boolean = Files.isWritable(path.toJavaPath())
override fun isExecutable(path: FsPath): Boolean = Files.isExecutable(path.toJavaPath())
override fun list(path: FsPath): Sequence<FsPath> = Files.list(path.toJavaPath()).asSequence().map { it.toFsPath() }
override fun walk(path: FsPath): Sequence<FsPath> = Files.walk(path.toJavaPath()).asSequence().map { it.toFsPath() }
override fun visit(path: FsPath, visitor: FsPathVisitor) =
Files.walkFileTree(path.toJavaPath(), JavaFsPathVisitorAdapter(visitor)).run {}
override fun readString(path: FsPath): String = Files.readString(path.toJavaPath())
override fun readAllBytes(path: FsPath): ByteArray = Files.readAllBytes(path.toJavaPath())
override fun readBytesChunked(path: FsPath, block: (ByteArray, Int) -> Unit) {
val javaPath = path.toJavaPath()
val stream = javaPath.inputStream()
val buffer = ByteArray(1024 * 1024)
var count: Int
while ((stream.read(buffer).also { count = it }) > 0) {
block(buffer, count)
}
}
override fun <T> readJsonFile(path: FsPath, deserializer: DeserializationStrategy<T>): T =
Json.decodeFromString(deserializer, readString(path))
override fun readLines(path: FsPath, block: (String) -> Unit) {
readLines(path).forEach(block)
}
override fun readLines(path: FsPath): Sequence<String> {
val stream = path.toJavaPath().bufferedReader()
return stream.lineSequence()
}
override fun <T> readJsonLinesToList(path: FsPath, deserializer: DeserializationStrategy<T>, block: (T) -> Unit) {
readLines(path) { line ->
val trimmed = line.trim()
val item = Json.decodeFromString(deserializer, trimmed)
block(item)
}
}
override fun <T> readJsonLinesToList(path: FsPath, deserializer: DeserializationStrategy<T>): List<T> {
val results = mutableListOf<T>()
readJsonLinesToList(path, deserializer) { item ->
results.add(item)
}
return results
}
override fun writeString(path: FsPath, content: String): Unit = Files.writeString(path.toJavaPath(), content).run {}
override fun writeAllBytes(path: FsPath, bytes: ByteArray): Unit = Files.write(path.toJavaPath(), bytes).run {}
override fun <T> writeJsonFile(path: FsPath, serializer: SerializationStrategy<T>, value: T) =
writeString(path, Json.encodeToString(serializer, value))
override fun delete(path: FsPath) {
path.toJavaPath().deleteExisting()
}
override fun deleteOnExit(path: FsPath) {
path.toJavaPath().toFile().deleteOnExit()
}
override fun deleteRecursively(path: FsPath) {
path.toJavaPath().toFile().deleteRecursively()
}
override fun createDirectories(path: FsPath) {
Files.createDirectories(path.toJavaPath())
}
}

View File

@ -0,0 +1,32 @@
package gay.pizza.dough.fs
import java.io.IOException
import java.nio.file.FileVisitResult
import java.nio.file.FileVisitor
import java.nio.file.Path
import java.nio.file.attribute.BasicFileAttributes
class JavaFsPathVisitorAdapter(private val visitor: FsPathVisitor) : FileVisitor<Path> {
override fun preVisitDirectory(dir: Path?, attrs: BasicFileAttributes?): FileVisitResult {
return visitor.beforeVisitDirectory(dir!!.toFsPath()).adapt()
}
override fun visitFile(file: Path?, attrs: BasicFileAttributes?): FileVisitResult {
return visitor.visitFile(file!!.toFsPath()).adapt()
}
override fun visitFileFailed(file: Path?, exc: IOException?): FileVisitResult {
return visitor.visitFileFailed(file!!.toFsPath(), exc!!).adapt()
}
override fun postVisitDirectory(dir: Path?, exc: IOException?): FileVisitResult {
return visitor.afterVisitDirectory(dir!!.toFsPath()).adapt()
}
private fun FsPathVisitor.VisitResult.adapt(): FileVisitResult = when (this) {
FsPathVisitor.VisitResult.Continue -> FileVisitResult.CONTINUE
FsPathVisitor.VisitResult.Terminate -> FileVisitResult.TERMINATE
FsPathVisitor.VisitResult.SkipSubtree -> FileVisitResult.SKIP_SUBTREE
FsPathVisitor.VisitResult.SkipSiblings -> FileVisitResult.SKIP_SIBLINGS
}
}

View File

@ -0,0 +1,37 @@
package gay.pizza.dough.fs
import java.nio.file.Path
import java.util.*
import kotlin.io.path.relativeTo
class JavaPath(val javaPath: Path) : FsPath {
override val fullPathString: String
get() = javaPath.toString()
override val entityNameString: String
get() = javaPath.fileName.toString()
override val parent: FsPath?
get() = javaPath.parent?.toFsPath()
override val operations: FsOperations = JavaFsOperations
override fun resolve(part: String): FsPath = javaPath.resolve(part).toFsPath()
override fun relativeTo(path: FsPath): FsPath = javaPath.relativeTo(path.toJavaPath()).toFsPath()
override fun toString(): String = javaPath.toString()
override fun equals(other: Any?): Boolean {
if (other == null) {
return false
}
if (other !is FsPath) {
return false
}
return other.toJavaPath() == javaPath
}
override fun hashCode(): Int = Objects.hash(javaPath)
}

View File

@ -0,0 +1,7 @@
package gay.pizza.dough.fs
import java.nio.file.Paths
actual fun FsPath(path: String): FsPath {
return JavaPath(Paths.get(path))
}