I implemented `relativePath` that Converts input path to a path relative to
baseDir.
[https://github.com/nim-lang/Nim/pull/8166](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
Run
I researched relativePath in other langauges:
[https://github.com/nim-lang/Nim/pull/8166#issuecomment-430974787](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](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".