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

Reply via email to