I implemented relativePath that Converts input path to a path relative to baseDir. https://github.com/nim-lang/Nim/pull/8166
Is my relativePath wrong? It should be implemented like one in other language? Is it worth to be in the Nim Standard Library?
This relativePath has 3 paramters but similar functions in other language have only 2 parameters. And this pull request was created on 1 Jul, but still it is S: Waiting on review status. I don't want to solve conflicts everytime lib/pure/ospaths.nim or tests/stdlib/tospaths.nim was changed.
I wrote why & when my implementation require 3 parameters in the comment:
proc relativePath*(path, baseDir: string; curDir: string = ""): string {.
noSideEffect, rtl, extern: "nos$1".} =
## Convert `path` to a path relative to baseDir.
##
## `path` and `baseDir` must be absolute paths or relative paths from `curDir`.
## When one of `path` and `baseDir` is relative and other one is absolute, `curDir` must be absolute.
##
## On DOS like filesystem, when a drive of `path` is different from `baseDir`,
## this proc just return the `path` as is because no way to calculate the relative path.
##
## Following pseudo code looks like Nim explains requirements of parameters.
##
## .. code-block:: nim
##
## if isAbsolute(path) and isAbsolute(baseDir):
## # `curDir` is ignored
## else not (isAbsolute(path) or isAbsolute(baseDir)):
## # Both `path` and `baseDir` must be relative to a same path.
## # Suppose ".." is only in front of path, not in middle of path.
## let numParDirsInPath = number of ".." in path
## let numParDirsInBaseDir = number of ".." in baseDir
## if numParDirsInBaseDir > numParDirsInPath:
## # `curDir` can be relative or absolute path.
## # Both `path` and `baseDir` must be relative paths from `curDir`.
## # `curDir` must has (numParDirsInBaseDir - numParDirsInPath) directories or raise ValueError.
## else:
## # `curDir` is ignored
## else:
## # `curDir` must be a absolute path.
## # `curDir` is used to convert `path` or `base` to a absolute path.
##
## For example, relativePath("a", "b") returns "../a", but relativePath("a", "..") raise exception.
## Because result of relativePath("a", "..") requires the parent directory name of "a".
##
## This proc never read filesystem.
## `baseDir` is always assumed to be a directory even if that path is actually a file.
##
## You can find more examples in tests/stdlib/tospaths.nim
runnableExamples:
doAssert relativePath("/home/abc".unixToNativePath, "/home/abc/x".unixToNativePath) == "..".unixToNativePath
doAssert relativePath("abc".unixToNativePath, "xyz".unixToNativePath, "".unixToNativePath) == "../abc".unixToNativePath
doAssert relativePath(".".unixToNativePath, "..".unixToNativePath, "/abc".unixToNativePath) == "abc".unixToNativePath
doAssert relativePath("/home/xyz/d".unixToNativePath, "xyz".unixToNativePath, "/home".unixToNativePath) == "d".unixToNativePath
doAssert relativePath("../d".unixToNativePath, "/usr".unixToNativePath, "/home/xyz".unixToNativePath) == "../home/d".unixToNativePath
I researched relativePath in other langauges: https://github.com/nim-lang/Nim/pull/8166#issuecomment-430974787
When path and baseDir are absolute paths, my implementation and other implementation works almost same way. When one of path xor baseDir are absolute paths, relative path is converted to absolute path using 3rd parameter (NOT using getCurrentDir) and a result is calculated from these 2 absolute paths. When both path and baseDir are relative paths, my implementation works differently from other implementation. My implementation doesn't call procedures that has side effect like getCurrentDir() or setCurrentDir(). When converting (path X relative to A) to (path Y relative to B) where X, Y, A and B are relative path, A and B are directories and B is relative to A, 3rd parameter is required if path Y includes parent directory of path X (In other words, path B has more ".." than path X). For example, if path X = ".", A = "a" and B = ".." then path Y = "a". If path X = "x", A = "a/b" and B = "../.." then path Y = "a/b/x". relative function in Boost filesystem takes 2 parameters and they are converted to absolute paths inside the function if they are relative path. https://www.boost.org/doc/libs/1_68_0/libs/filesystem/src/operations.cpp Converting a relative path to a absolute path requires getting current working directory that cannot be done from noSideEffect procedure. It cannot convert relative paths that does not exists in filesystem. When converting a relative path to a absolute path, that could prepend directory names that don't affect resulting path. For example, if current working directroy was "/somedir/a/b", path X = "x" become "/somedir/a/b/x" and path B = "../../" become "/somedir". Then, relative function in Boost filesystem convert "/somedir/a/b/x" to "a/b/x" which is relative to "/somedir". So it need to allocate more memory to store string "/somedir" and require more comparsion to calcute relative path. My relativePath works without converting path to absolute path and doesn't needs to store unnecessary string "/somedir". It only requires 3rd parameter "a/b".