Your `type Path = distinct string` can be a good idea. Instead of creating a
new types corresponding to a relative/absolute path, file/directory and symlink
by inheriting from `Path`, generic distinct type might be better. Because if
you create these types by inheritance, a number of types can be large. Generic
distinct types works like a type with parameters.
In this code, I used a bool type for `IsAbs`, `IsFile` and `IsSym` generic
parameters, but an enum type like `enum true, false, unknown` might be better
than bool, because there are cases you don't need to care whether a path is
relative or absolute, symlink or not.
I think this idea should be tested as nimble library before implementing it as
standard library. If you find this was actually not a good idea after
implementing it in standard library and some people used it in some projects,
it is not easy to remove it from standard library because Nim have to keep
compatibility as long as there are people using it.
import os
type
Path[IsAbs, IsFile: static[bool], IsSym, IsDev: static[bool] = false] =
distinct string
func isEmpty(x: Path): bool {.inline.} = x.string.len == 0
proc absolutePath(path: string; IsFile: static[bool]; IsSym, IsDev:
static[bool] = false): auto =
assert os.isValidFilename(path)
Path[true, IsFile, IsSym, IsDev](os.absolutePath(path))
proc absoluteFile(path: string; IsSym, IsDev: static[bool] = false): auto =
path.absolutePath(true, IsSym, IsDev)
proc absoluteDir(path: string; IsSym: static[bool] = false): auto =
path.absolutePath(false, IsSym)
# This code requires this issue fixed:
https://github.com/nim-lang/Nim/issues/1385
proc copy(src: Path; dst: distinct Path) =
when src.IsDev or dst.IsDev:
{.fatal: "device file cannot be copied!"}
when src.IsFile and dst.IsFile:
os.copyFile(src.string, dst.string)
elif src.IsFile and not dst.IsFile:
os.copyFileToDir(src.string, dst.string)
elif not src.IsFile and not dst.IsFile:
os.copyDir(src.string, dst.string)
else:
{.fatal: "I cannot copy a directory to a file"}
let foo = Path[false, true, false, false]"foo.nim"
echo foo.isEmpty
# Make relative path to absolute path
let bar = absoluteFile"bar.nim"
echo bar.string
let
src = absoluteFile"/tmp/foo"
dst = absoluteFile"/tmp/bar"
# Copy a file to a file
copy(src, dst)
let
someDir = absoluteDir"/tmp/baz"
# Copy a file into a directory
copy(src, someDir)
# Compile time error because trying to copy a directory to a file.
#copy(someDir, dst)
let dev = Path[true, true, IsDev = true]"/dev/null"
# Compile time error because copying a device file
#copy(dev, someDir)
# Compile time error
#copy(dev, foo)
Run