diff --git a/doc/batched_sequences.md b/doc/batched_sequences.md index 7fbe109..d470a64 100644 --- a/doc/batched_sequences.md +++ b/doc/batched_sequences.md @@ -6,7 +6,8 @@ to drastically decrease the amount of single reads in the original source. Example: ```kt -File("text.txt") // File +Path("text.txt") // Path + .getFile() // File .openRead() // BatchSequence .batched(64) // BatchSequence ``` @@ -21,6 +22,7 @@ Example: val data = myData // Sequence .asBatch() // BatchSequence -File("text.txt") +Path("text.txt") // Path + .getOrCreateFile() // File .write(data) ``` \ No newline at end of file diff --git a/src/main/kotlin/blitz/Either.kt b/src/main/kotlin/blitz/Either.kt index e4b3dc1..6ac1760 100644 --- a/src/main/kotlin/blitz/Either.kt +++ b/src/main/kotlin/blitz/Either.kt @@ -32,10 +32,10 @@ class Either private constructor( if (isA) af(a!!.v) else bf(b!!.v) fun mapA(transform: (A) -> RA): Either = - Either(a.map(transform), b) + Either(a.mapNotNull(transform), b) fun mapB(transform: (B) -> RB): Either = - Either(a, b.map(transform)) + Either(a, b.mapNotNull(transform)) override fun toString(): String = if (isA) "Either($a)" diff --git a/src/main/kotlin/blitz/Fnp.kt b/src/main/kotlin/blitz/Fnp.kt index f722d30..34fad06 100644 --- a/src/main/kotlin/blitz/Fnp.kt +++ b/src/main/kotlin/blitz/Fnp.kt @@ -1,5 +1,8 @@ package blitz +import blitz.func.* +import blitz.func.io.* + fun main(args: Array) { // pureCat(args) // .impure() diff --git a/src/main/kotlin/blitz/ModifyException.kt b/src/main/kotlin/blitz/ModifyException.kt new file mode 100644 index 0000000..0f3346d --- /dev/null +++ b/src/main/kotlin/blitz/ModifyException.kt @@ -0,0 +1,8 @@ +package blitz + +fun modifyException(new: Throwable, block: () -> R): R = + try { + block() + } catch (e: Throwable) { + throw new + } \ No newline at end of file diff --git a/src/main/kotlin/blitz/func/Multiples.kt b/src/main/kotlin/blitz/func/Multiples.kt new file mode 100644 index 0000000..4c13675 --- /dev/null +++ b/src/main/kotlin/blitz/func/Multiples.kt @@ -0,0 +1,4 @@ +package blitz.func + +infix fun Pair.tri(c: C): Triple = + Triple(first, second, c) \ No newline at end of file diff --git a/src/main/kotlin/blitz/func/Rewrap.kt b/src/main/kotlin/blitz/func/Rewrap.kt index 85a5bc7..e168256 100644 --- a/src/main/kotlin/blitz/func/Rewrap.kt +++ b/src/main/kotlin/blitz/func/Rewrap.kt @@ -14,3 +14,19 @@ fun Sequence>.rewrap(): Monad> = val iter = this@rewrap.iterator() sequence { if (iter.hasNext()) yield(iter.next().impure()) } } + +fun Monad>.rewrap(): Pair, Monad> { + val v = lazy { impure() } + return Monad { v.value.first } to Monad { v.value.second } +} + +fun Pair, Monad>.rewrap(): Monad> = + Monad { first.impure() to second.impure() } + +fun Monad>.rewrap(): Triple, Monad, Monad> { + val v = lazy { impure() } + return Monad { v.value.first } to Monad { v.value.second } tri Monad { v.value.third } +} + +fun Triple, Monad, Monad>.rewrap(): Monad> = + Monad { first.impure() to second.impure() tri third.impure() } \ No newline at end of file diff --git a/src/main/kotlin/blitz/func/io/File.kt b/src/main/kotlin/blitz/func/io/File.kt index d13b880..69c8d02 100644 --- a/src/main/kotlin/blitz/func/io/File.kt +++ b/src/main/kotlin/blitz/func/io/File.kt @@ -1,13 +1,32 @@ package blitz.func.io -import blitz.func.Monad -import blitz.func.bind -import blitz.io.readerSequence -import kotlinx.io.files.Path -import kotlinx.io.files.SystemFileSystem +import blitz.* +import blitz.func.* +import blitz.io.* -fun Monad.asPath() = - bind { Path(it) } +fun Monad.asPath(): Monad = + bind { Path.of(it) } -fun Monad.read() = - bind { p -> { SystemFileSystem.source(p) }.readerSequence() } \ No newline at end of file +fun Monad.read(): Monad = + bind { it.getFile().read() } + +fun Monad.write(seq: Monad): Monad = + bind { it.getFile().write(seq.impure()) } + +fun Monad.append(seq: Monad): Monad = + bind { it.getFile().append(seq.impure()) } + +fun Monad.writeTo(path: Monad): Monad = + path.write(this) + +fun Monad.appendTo(path: Monad): Monad = + path.append(this) + +fun Monad.exists(): Monad = + bind { it.exists() } + +fun Monad.isDir(): Monad = + bind { it.isDir() } + +fun Monad.isFile(): Monad = + bind { it.isFile() } \ No newline at end of file diff --git a/src/main/kotlin/blitz/io/Dir.kt b/src/main/kotlin/blitz/io/Dir.kt new file mode 100644 index 0000000..83853c2 --- /dev/null +++ b/src/main/kotlin/blitz/io/Dir.kt @@ -0,0 +1,6 @@ +package blitz.io + +@JvmInline +value class Dir internal constructor( + val path: Path +) \ No newline at end of file diff --git a/src/main/kotlin/blitz/io/File.kt b/src/main/kotlin/blitz/io/File.kt new file mode 100644 index 0000000..dfbddd8 --- /dev/null +++ b/src/main/kotlin/blitz/io/File.kt @@ -0,0 +1,34 @@ +package blitz.io + +import blitz.ByteBatchSequence +import kotlinx.io.Buffer +import kotlinx.io.files.SystemFileSystem + +@JvmInline +value class File internal constructor( + val path: Path +) + +fun File.read(): ByteBatchSequence = + path.toKotlinxPath().let { + { SystemFileSystem.source(it) } + }.readerSequence() + +private fun File.writeAppendFrom(seq: ByteBatchSequence, append: Boolean) { + val path = path.toKotlinxPath() + SystemFileSystem.sink(path, append).use { sink -> + val iter = seq.iterator() + while (iter.hasNext()) { + val batch = iter.nextBytes(8192) + val buff = Buffer() + buff.write(batch) + sink.write(buff, buff.size) + } + } +} + +fun File.write(seq: ByteBatchSequence) = + writeAppendFrom(seq, false) + +fun File.append(seq: ByteBatchSequence) = + writeAppendFrom(seq, true) \ No newline at end of file diff --git a/src/main/kotlin/blitz/io/Path.kt b/src/main/kotlin/blitz/io/Path.kt new file mode 100644 index 0000000..c034cc5 --- /dev/null +++ b/src/main/kotlin/blitz/io/Path.kt @@ -0,0 +1,91 @@ +package blitz.io + +import blitz.modifyException +import kotlinx.io.files.SystemFileSystem +import kotlinx.io.files.SystemPathSeparator + +/** + * Every instance of this class represents an absolute path in a file system. + */ +@JvmInline +value class Path( + val parts: Array +) { + val full: String + get() = parts.joinToString(separator.toString()) + + val name: String + get() = parts.last() + + fun parent(): Path? = + if (parts.isEmpty()) null + else Path(parts.dropLast(1).toTypedArray()) + + // TODO: remove "." and ".." from the path + fun child(vararg paths: String): Path = + modifyException(Exception("children in Path.child() can not contain path separator!")) { + Path(parts + paths.onEach { assert(separator !in it) }) + } + + fun exists(): Boolean = + SystemFileSystem.exists(toKotlinxPath()) + + internal fun toKotlinxPath() = + kotlinx.io.files.Path("", *parts) + + private fun kotlinxMeta() = + SystemFileSystem.metadataOrNull(toKotlinxPath()) + + fun isDir() = + kotlinxMeta()?.isDirectory ?: false + + fun isFile() = + kotlinxMeta()?.isRegularFile ?: false + + fun getDir(): Dir { + if (!exists()) throw Exception("Path does not exist!") + return Dir(this) + } + + fun getOrCreateDir(): Dir { + if (!exists()) SystemFileSystem.createDirectories(toKotlinxPath()) + return Dir(this) + } + + fun getFile(): File { + if (!exists()) throw Exception("Path does not exist!") + return File(this) + } + + fun getOrCreateFile(): File { + if (!exists()) SystemFileSystem.sink(toKotlinxPath()).flush() + return File(this) + } + + fun deleteFileOrDir() { + if (!exists()) throw Exception("Path does not exist!") + SystemFileSystem.delete(toKotlinxPath()) + } + + fun tryDeleteFileOrDir() { + try { + if (exists()) + SystemFileSystem.delete(toKotlinxPath()) + } catch (_: Exception) {} + } + + companion object { + val separator: Char = SystemPathSeparator + + /** + * Creates a path from a relative or absolute path string. + */ + fun of(path: String): Path { + if (path.isEmpty()) return Path(emptyArray()) + val kx = kotlinx.io.files.Path(path) + if (kx.isAbsolute) + return Path(path.substring(1).split(separator).toTypedArray()) + return of(SystemFileSystem.resolve(kx).toString()) + } + } +} \ No newline at end of file