diff options
author | nea <romangraef@gmail.com> | 2021-08-12 23:37:47 +0200 |
---|---|---|
committer | nea <romangraef@gmail.com> | 2021-08-12 23:37:47 +0200 |
commit | 2ee4b7c2bef9f80ed12e58bc3cdbb965299920e1 (patch) | |
tree | d4ac288bb7c9e15acfd9cfa1868a69b0b15fc30e /src/main | |
parent | 14f72a3d6005dec3f9fc1e9832161b5de0d1b354 (diff) | |
download | webos-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.kt | 75 | ||||
-rw-r--r-- | src/main/kotlin/WebOS.kt | 102 | ||||
-rw-r--r-- | src/main/kotlin/files.kt | 86 | ||||
-rw-r--r-- | src/main/kotlin/util/sequence.kt | 5 |
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() + |