Skip to content

Commit

Permalink
Add path resolution of slashes for File instantiation
Browse files Browse the repository at this point in the history
  • Loading branch information
05nelsonm committed Dec 10, 2023
1 parent 348b36a commit 502a094
Show file tree
Hide file tree
Showing 11 changed files with 201 additions and 103 deletions.
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,13 @@ fun commonMain(f: File) {
f.canonicalPath()
f.canonicalFile()

// equivalent to File("some/path")
"some/path".toFile()
// equivalent to File("some", "path")
"some".toFile("path")
// equivalent to File("/some/path")
val file = "/some/path".toFile()

// resolve child paths
val child = f.resolve("child")
child.resolve(f)
val child = file.resolve("child")
println(child.path) // >> `/some/path/child`
println(child.resolve(file).path) // >> `/some/path` (file is rooted)

// normalized File (e.g. removal of . and ..)
f.normalize()
Expand Down
1 change: 0 additions & 1 deletion library/file/api/file.api
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ public final class io/matthewnelson/kmp/file/File {
public static final fun path (Ljava/io/File;)Ljava/lang/String;
public static final fun resolve (Ljava/io/File;Ljava/lang/String;)Ljava/io/File;
public static final fun toFile (Ljava/lang/String;)Ljava/io/File;
public static final fun toFile (Ljava/lang/String;Ljava/lang/String;)Ljava/io/File;
}

public final class io/matthewnelson/kmp/file/FileJvm {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,37 @@ public expect val SysPathSep: Char
* */
public expect val SysTempDir: File

public fun String.toFile(): File = File(this)

/**
* A File
* */
public expect class File {

public constructor(pathname: String)

/**
* Concatenates [parent] with [child]
* */
public constructor(parent: String, child: String)

/**
* Concatenates [parent] path with [child]
* */
public constructor(parent: File, child: String)
public expect class File(pathname: String) {

// Not exposing any secondary constructors because
// Jvm has undocumented behavior that cannot be
// modified because it's typealias.
//
// java.io.File's secondary constructors take 2
// arguments and concatenate the paths together. If
// the first argument is empty though, the result
// will always contain the system path separator as
// the first character.
//
// println(File("", "child").path) >> "/child"
// println(File(File(""), "child").path) >> "/child"
// println(File("", "./child").path) >> "/./child"
//
// So for Unix, the "child" argument now magically
// becomes absolute instead of relative to the current
// working directory.
//
// This could be dangerous if someone were to do:
//
// File(fileFromSomewhereElse, "child")
//
// thinking that it would simply be appended to the
// parent.

public fun isAbsolute(): Boolean

Expand Down Expand Up @@ -76,9 +91,6 @@ public expect class File {
internal fun getCanonicalFile(): File
}

public fun String.toFile(): File = File(this)
public fun String.toFile(child: String): File = File(this, child)

@get:JvmName("name")
public val File.name: String get() = getName()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ class AbsoluteUnitTest {
assertFalse("C:".toFile().isAbsolute())
assertFalse("".toFile().isAbsolute())
assertFalse(".".toFile().isAbsolute())
assertFalse("..".toFile().isAbsolute())
assertFalse("./something".toFile().isAbsolute())
assertFalse("../something".toFile().isAbsolute())
assertFalse("some/path".toFile().isAbsolute())

// TODO: Fix isAbsolute for Nodejs on windows
Expand All @@ -52,8 +54,8 @@ class AbsoluteUnitTest {
if (isSimulator) return

val rootDir = PROJECT_DIR_PATH.substringBeforeLast(
"library"
.toFile("file")
"library".toFile()
.resolve("file")
.path
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,47 @@ import kotlin.test.assertEquals

class FileUnitTest {

// TODO: Implement toPath() (Native)
// @Test
// fun givenFile_whenTrailingSlashes_thenAreRemoved() {
// val expected = "expected"
//
// (0..5).forEach { times ->
// val file = File(buildString {
// append(expected)
// repeat(times) { append(SYSTEM_PATH_SEPARATOR) }
// })
//
// assertEquals(expected, file.path)
// }
// }

@Test
fun givenFile_whenToString_thenPrintsPath() {
val expected = "something"
assertEquals(expected, expected.toFile().toString())
}

@Test
fun givenFile_whenInsaneSlashes_thenAreResolved() {
// windows should replace all unix path separators with `\` before
assertEquals("relative${SysPathSep}path", "relative////path///".toFile().path)
assertEquals("relative${SysPathSep}path${SysPathSep}.", "relative////path///.".toFile().path)
assertEquals(".${SysPathSep}..", "./..".toFile().path)

assertEquals(".", ".".toFile().path)
assertEquals("..", "..".toFile().path)
assertEquals("...", "...".toFile().path)
assertEquals("....", "....".toFile().path)

if (isWindows) {
assertEquals("\\", "\\".toFile().path)
assertEquals("\\Relative", "\\Relative".toFile().path)
assertEquals("\\Relative", "/Relative".toFile().path)
assertEquals("\\Relative\\path", "/Relative/path".toFile().path)

assertEquals("\\\\", "\\\\".toFile().path)
assertEquals("\\\\", "\\\\\\".toFile().path)
assertEquals("\\\\Absolute", "\\\\Absolute".toFile().path)
assertEquals("\\\\Absolute", "\\\\\\Absolute".toFile().path)
assertEquals("\\\\Absolute", "//Absolute".toFile().path)
assertEquals("\\\\Absolute", "///Absolute".toFile().path)
assertEquals("\\\\Absolute\\path", "///Absolute//path".toFile().path)

assertEquals("C:\\", "C://".toFile().path)
assertEquals("F:", "F:".toFile().path)
assertEquals("F:\\", "F:\\\\\\".toFile().path)
} else {
assertEquals("/", "/".toFile().path)
assertEquals("\\", "\\".toFile().path)
assertEquals("\\\\", "\\\\".toFile().path)
assertEquals("/absolute", "//absolute".toFile().path)
assertEquals("/absolute\\/path", "///absolute\\////path".toFile().path)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,13 @@ class ParentUnitTest {
assertEquals(expected, expected.toFile().resolve("anything").parentPath)
}

// TODO: Implement toPath() (Native)
// @Test
// fun givenFile_whenHasTrailingSlashes_thenIgnoresThem() {
// val path = buildString {
// append(' ')
// repeat(3) { append(SYSTEM_PATH_SEPARATOR) }
// }
//
// assertNull(File(path).parent)
// }
@Test
fun givenFile_whenHasTrailingSlashes_thenIgnoresThem() {
val path = buildString {
append(' ')
repeat(3) { append(SysPathSep) }
}

assertNull(path.toFile().parentPath)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,31 @@
**/
package io.matthewnelson.kmp.file

import kotlin.test.Test
import kotlin.test.assertEquals

class ResolveUnitTest {

// TODO
@Test
fun givenFile_whenResolve_thenIsExpected() {
assertEquals("", "".toFile().resolve("").path)
assertEquals("c", "".toFile().resolve("c").path)
assertEquals("p${SysPathSep}c", "p".toFile().resolve("c").path)
assertEquals("p${SysPathSep}..", "p".toFile().resolve("..").path)
assertEquals("p${SysPathSep}p2${SysPathSep}..", "p".toFile().resolve("p2").resolve("..").path)


// TODO: Issue #9
if (isNodejs && isWindows) return

val expected = if (isWindows) {
"p${SysPathSep}c"
} else {
// Unix the relative file is absolute
// so should be returned.
"${SysPathSep}c"
}

assertEquals(expected, "p".toFile().resolve("${SysPathSep}c").path)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ import kotlin.io.resolve as _resolve
import kotlin.io.writeBytes as _writeBytes
import kotlin.io.writeText as _writeText

public actual typealias File = java.io.File

@JvmField
public actual val SysPathSep: Char = File.separatorChar

Expand All @@ -35,6 +33,8 @@ public actual val SysTempDir: File = System
.getProperty("java.io.tmpdir")
.toFile()

public actual typealias File = java.io.File

public actual fun File.normalize(): File = _normalize()

@Throws(IOException::class)
Expand Down
Loading

0 comments on commit 502a094

Please sign in to comment.