summaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authornea <romangraef@gmail.com>2021-08-12 23:37:47 +0200
committernea <romangraef@gmail.com>2021-08-12 23:37:47 +0200
commit2ee4b7c2bef9f80ed12e58bc3cdbb965299920e1 (patch)
treed4ac288bb7c9e15acfd9cfa1868a69b0b15fc30e /src/main
parent14f72a3d6005dec3f9fc1e9832161b5de0d1b354 (diff)
downloadwebos-2ee4b7c2bef9f80ed12e58bc3cdbb965299920e1.tar.gz
webos-2ee4b7c2bef9f80ed12e58bc3cdbb965299920e1.tar.bz2
webos-2ee4b7c2bef9f80ed12e58bc3cdbb965299920e1.zip
file system rework
Diffstat (limited to 'src/main')
-rw-r--r--src/main/kotlin/Path.kt75
-rw-r--r--src/main/kotlin/WebOS.kt102
-rw-r--r--src/main/kotlin/files.kt86
-rw-r--r--src/main/kotlin/util/sequence.kt5
4 files changed, 174 insertions, 94 deletions
diff --git a/src/main/kotlin/Path.kt b/src/main/kotlin/Path.kt
new file mode 100644
index 0000000..0e209cf
--- /dev/null
+++ b/src/main/kotlin/Path.kt
@@ -0,0 +1,75 @@
+import util.expandWith
+
+sealed interface Path {
+ val parts: List<String>
+ fun toAbsolutePath(relativeTo: Absolute): Absolute {
+ return relativeTo.resolve(this)
+ }
+
+ fun resolve(path: Path): Path
+
+ companion object {
+ val root = Absolute(listOf())
+
+ fun ofShell(string: String, userHome: Absolute): Path =
+ ofShell(string.split("/"), userHome)
+
+ fun ofShell(vararg parts: String, userHome: Absolute): Path =
+ ofShell(parts.toList(), userHome)
+
+ fun of(vararg parts: String): Path =
+ of(parts.toList())
+
+ fun of(string: String): Path =
+ of(string.split("/"))
+
+ fun ofShell(parts: List<String>, userHome: Absolute): Path {
+ if (parts.firstOrNull() == "~")
+ return userHome.resolve(Relative(parts.subList(1, parts.size).filter { it.isNotEmpty() }))
+ return of(parts)
+ }
+
+ fun of(parts: List<String>): Path {
+ if (parts.isEmpty())
+ return root
+ if (parts[0] == "") // Starts with a /
+ return Absolute(parts.subList(1, parts.size).filter { it.isNotEmpty() })
+ return Relative(parts.filter { it.isNotEmpty() })
+ }
+ }
+
+ data class Relative internal constructor(override val parts: List<String>) : Path {
+ override fun resolve(path: Path): Path {
+ if (path is Absolute) return path
+ return Relative(this.parts + path.parts)
+ }
+ }
+
+ data class Absolute internal constructor(override val parts: List<String>) : Path {
+ override fun resolve(path: Path): Absolute {
+ if (path is Absolute) return path
+ return Absolute(this.parts + path.parts)
+ }
+
+ fun relativize(path: Path): Relative = when (path) {
+ is Relative -> path
+ is Absolute -> {
+ var commonPrefix = true
+ val partList = mutableListOf<String>()
+ var returns = 0
+ for ((idx, part) in path.parts.withIndex()) {
+ if (idx < this.parts.size) {
+ if (this.parts[idx] == part && commonPrefix) {
+ continue
+ } else {
+ commonPrefix = false
+ returns++
+ }
+ }
+ partList.add(part)
+ }
+ Relative(List(returns) { ".." } + partList)
+ }
+ }
+ }
+}
diff --git a/src/main/kotlin/WebOS.kt b/src/main/kotlin/WebOS.kt
index 6fe4ceb..9160a75 100644
--- a/src/main/kotlin/WebOS.kt
+++ b/src/main/kotlin/WebOS.kt
@@ -29,15 +29,15 @@ class Console(val os: WebOS, val renderElement: Element?) {
var shouldRerender = true
- lateinit var currentUser: User
+ var currentUser: User? = null
- private var _workingDirectory: AbsolutPath? = null
+ private var _workingDirectory: Path.Absolute? = null
- var workingDirectory: AbsolutPath
- get() {
- if (_workingDirectory == null) _workingDirectory = currentUser.homeDirectory; return _workingDirectory!!
+ var workingDirectory: Path.Absolute
+ get() = _workingDirectory ?: currentUser?.homeDirectory ?: Path.root
+ set(value) {
+ _workingDirectory = value
}
- set(value) { _workingDirectory = value }
fun openActivity(activity: Activity) {
activityStack.addLast(activity)
@@ -67,7 +67,7 @@ class Console(val os: WebOS, val renderElement: Element?) {
class WebOS {
private val _consoles = mutableListOf<Console>()
val consoles get() = _consoles.toList()
- val IOHandler = IOHandler()
+ val files = IOHandler()
fun registerConsole(element: Element) {
_consoles.add(Console(this, element))
}
@@ -75,92 +75,6 @@ class WebOS {
data class User(
val name: String,
- val homeDirectory: AbsolutPath
+ val homeDirectory: Path.Absolute
)
-data class RelativePath internal constructor(override val parts: List<String>) : Path {
- override fun toAbsolutPath(context: Console): AbsolutPath {
- val result = mutableListOf<String>()
- for (part in parts) {
- when (part) {
- "." -> result.addAll(context.workingDirectory.parts)
- "~" -> result.addAll(context.currentUser.homeDirectory.parts)
- ".." -> { result.removeLast() }
- else -> result.add(part)
- }
- }
- return AbsolutPath(result)
- }
-}
-
-data class AbsolutPath internal constructor(override val parts: List<String>) : Path {
- override fun toAbsolutPath(context: Console): AbsolutPath = this
-}
-
-sealed interface Path {
- val parts: List<String>
- fun toAbsolutPath(context: Console): AbsolutPath
- companion object {
- fun of(string: String): Path {
- val isAbsolut = string.first() == '/'
- val parts = string.split("/")
- val aparts = parts.subList(1, parts.size)
- if (aparts.contains(".") || aparts.contains("~"))
- throw IllegalArgumentException("'.' and '~' are only allowed at the beginning of a relative path")
- if (!isAbsolut) return RelativePath(parts)
- if (aparts.contains("..")) throw IllegalArgumentException("'..' is only allowed in a relative path")
- return AbsolutPath(aparts)
- }
- }
-}
-
-class FileSystem {
- fun read(relativePath: Path): ReadResult = TODO()
- fun write(path: Path, data: ByteArray): Unit = TODO()
- fun stat(path: Path): Unit = TODO()
-}
-
-class IOHandler {
- val mounts = mutableListOf<Mount>()
- fun mount(absolutPath: AbsolutPath, fileSystem: FileSystem) {
- mounts += Mount(absolutPath, fileSystem)
- }
- fun unmount(mountPoint: AbsolutPath) {
- mounts.removeAll { it.mountPoint == mountPoint }
- }
- fun <T> findMountFor(context: Console, path: Path, operation: FileSystem.(relativePath: Path) -> T): T {
- val absolutPath = path.toAbsolutPath(context)
- val mount = mounts.filter {
- var result = true
- it.mountPoint.parts.forEachIndexed { index, part -> if (absolutPath.parts[index] != part) {
- result = false
- } }
- result
- }.maxByOrNull { it.mountPoint.parts.size} ?: throw IllegalStateException("No mount present")
- return mount.fileSystem.operation(
- AbsolutPath(absolutPath.parts.subList(mount.mountPoint.parts.size, absolutPath.parts.size))
- )
- }
-
- fun read(context: Console, path: Path): ReadResult = findMountFor(context, path) { read(it) }
- fun write(context: Console, path: Path, data: ByteArray): Unit = findMountFor(context, path) { write(it, data) }
- fun stat(context: Console, path: Path): Unit = findMountFor(context, path) { stat(it) }
-}
-
-sealed class ReadResult {
- class Success(val text: String) : ReadResult()
- object NotFound
- object NoAccess
-}
-data class Mount (
- val mountPoint: AbsolutPath,
- val fileSystem: FileSystem
-)
-
-data class Stat(
- val exists: Boolean,
- var owner: User,
- val created: Long,
- val edited: Long,
- val size: Long
-)
diff --git a/src/main/kotlin/files.kt b/src/main/kotlin/files.kt
new file mode 100644
index 0000000..990ae7e
--- /dev/null
+++ b/src/main/kotlin/files.kt
@@ -0,0 +1,86 @@
+class IOHandler {
+ val mounts = mutableListOf<Mount>()
+ fun mount(absolutePath: Path.Absolute, fileSystem: FileSystem) {
+ if (mounts.any { it.mountPoint == absolutePath })
+ return // TODO sensible error message handling
+ mounts += Mount(absolutePath, fileSystem)
+ }
+
+ fun unmount(mountPoint: Path.Absolute) {
+ mounts.removeAll { it.mountPoint == mountPoint }
+ }
+
+ fun <T> findMountFor(
+ workingDirectory: Path.Absolute,
+ path: Path,
+ operation: FileSystem.(relativePath: Path) -> T
+ ): T {
+ val absolutPath = path.toAbsolutePath(workingDirectory)
+ val mount = mounts.filter {
+ it.mountPoint.parts.zip(absolutPath.parts).all { (a, b) -> a == b }
+ }.maxByOrNull { it.mountPoint.parts.size } ?: throw IllegalStateException("No mount present")
+ return mount.fileSystem.operation(
+ Path.Absolute(
+ absolutPath.parts.subList(
+ mount.mountPoint.parts.size,
+ absolutPath.parts.size
+ )
+ ) // TODO: unangenehm
+ )
+ }
+
+ fun findINode(absolutePath: Path.Absolute): INode {
+ val mount = mounts.filter {
+ it.mountPoint.parts.zip(absolutePath.parts).all { (a, b) -> a == b }
+ }.maxByOrNull { it.mountPoint.parts.size } ?: throw IllegalStateException("No mount present")
+ val iNode = mount.fileSystem.getINode(absolutePath.relativize(mount.mountPoint))
+ return when (iNode) {
+ is INodeResult.File -> iNode.op
+ is INodeResult.ResolveAgain -> findINode(absolutePath.resolve(iNode.relativeToOriginal))
+ }
+ }
+
+ fun read(workingDirectory: Path.Absolute, path: Path): ReadResult =
+ findMountFor(workingDirectory, path) { read(it) }
+
+ fun write(workingDirectory: Path.Absolute, path: Path, data: ByteArray): Unit =
+ findMountFor(workingDirectory, path) { write(it, data) }
+
+ fun stat(workingDirectory: Path.Absolute, path: Path): Unit =
+ findMountFor(workingDirectory, path) { stat(it) }
+}
+
+interface INode {
+ val fs: FileSystem
+}
+
+sealed interface INodeResult {
+ class File(val op: INode) : INodeResult
+ class ResolveAgain(val relativeToOriginal: Path): INodeResult
+}
+
+interface FileSystem {
+ fun getINode(relativePath: Path.Relative): INodeResult
+ fun read(relativePath: Path): ReadResult
+ fun write(path: Path, data: ByteArray): Unit // Write result
+ fun stat(path: Path): Unit // TODO Stat result
+}
+
+sealed class ReadResult {
+ class Success(val text: String) : ReadResult()
+ object NotFound : ReadResult()
+ object NoAccess : ReadResult()
+}
+
+data class Mount(
+ val mountPoint: Path.Absolute,
+ val fileSystem: FileSystem
+)
+
+data class Stat(
+ val exists: Boolean,
+ var owner: User,
+ val created: Long,
+ val edited: Long,
+ val size: Long
+)
diff --git a/src/main/kotlin/util/sequence.kt b/src/main/kotlin/util/sequence.kt
new file mode 100644
index 0000000..72b07dc
--- /dev/null
+++ b/src/main/kotlin/util/sequence.kt
@@ -0,0 +1,5 @@
+package util
+
+fun <T> Iterable<T>.expandWith(t: T): Sequence<T> =
+ this.asSequence() + generateSequence { t }.asSequence()
+