summary refs log tree commit diff stats
path: root/compiler/depends.nim
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2013-12-25 14:53:00 +0100
committerAraq <rumpf_a@web.de>2013-12-25 14:53:00 +0100
commit9e92455a534956bfbb0a7ec5e6f2bdffd7268818 (patch)
tree30e405f1340f8ffc146dcc420304bf305d38de8f /compiler/depends.nim
parent4d01408a4643a482af59c620add2a9e59f45c8f4 (diff)
downloadNim-9e92455a534956bfbb0a7ec5e6f2bdffd7268818.tar.gz
templates can access hidden fields
Diffstat (limited to 'compiler/depends.nim')
0 files changed, 0 insertions, 0 deletions
href='#n106'>106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
#
#
#           The Nim Compiler
#        (c) Copyright 2018 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## Path handling utilities for Nim. Strictly typed code in order
## to avoid the never ending time sink in getting path handling right.

import os, pathnorm, strutils

when defined(nimPreviewSlimSystem):
  import std/[syncio, assertions]

type
  AbsoluteFile* = distinct string
  AbsoluteDir* = distinct string
  RelativeFile* = distinct string
  RelativeDir* = distinct string
  AnyPath* = AbsoluteFile|AbsoluteDir|RelativeFile|RelativeDir

proc isEmpty*(x: AnyPath): bool {.inline.} = x.string.len == 0

proc copyFile*(source, dest: AbsoluteFile) =
  os.copyFile(source.string, dest.string)

proc removeFile*(x: AbsoluteFile) {.borrow.}

proc splitFile*(x: AbsoluteFile): tuple[dir: AbsoluteDir, name, ext: string] =
  let (a, b, c) = splitFile(x.string)
  result = (dir: AbsoluteDir(a), name: b, ext: c)

proc extractFilename*(x: AbsoluteFile): string {.borrow.}

proc fileExists*(x: AbsoluteFile): bool {.borrow.}
proc dirExists*(x: AbsoluteDir): bool {.borrow.}

proc quoteShell*(x: AbsoluteFile): string {.borrow.}
proc quoteShell*(x: AbsoluteDir): string {.borrow.}

proc cmpPaths*(x, y: AbsoluteDir): int {.borrow.}

proc createDir*(x: AbsoluteDir) {.borrow.}

proc toAbsoluteDir*(path: string): AbsoluteDir =
  result = if path.isAbsolute: AbsoluteDir(path)
           else: AbsoluteDir(getCurrentDir() / path)

proc `$`*(x: AnyPath): string = x.string

when true:
  proc eqImpl(x, y: string): bool {.inline.} =
    result = cmpPaths(x, y) == 0

  proc `==`*[T: AnyPath](x, y: T): bool = eqImpl(x.string, y.string)

  template postProcessBase(base: AbsoluteDir): untyped =
    # xxx: as argued here https://github.com/nim-lang/Nim/pull/10018#issuecomment-448192956
    # empty paths should not mean `cwd` so the correct behavior would be to throw
    # here and make sure `outDir` is always correctly initialized; for now
    # we simply preserve pre-existing external semantics and treat it as `cwd`
    when false:
      doAssert isAbsolute(base.string), base.string
      base
    else:
      if base.isEmpty: getCurrentDir().AbsoluteDir else: base

  proc `/`*(base: AbsoluteDir; f: RelativeFile): AbsoluteFile =
    let base = postProcessBase(base)
    assert(not isAbsolute(f.string), f.string)
    result = AbsoluteFile newStringOfCap(base.string.len + f.string.len)
    var state = 0
    addNormalizePath(base.string, result.string, state)
    addNormalizePath(f.string, result.string, state)

  proc `/`*(base: AbsoluteDir; f: RelativeDir): AbsoluteDir =
    let base = postProcessBase(base)
    assert(not isAbsolute(f.string))
    result = AbsoluteDir newStringOfCap(base.string.len + f.string.len)
    var state = 0
    addNormalizePath(base.string, result.string, state)
    addNormalizePath(f.string, result.string, state)

  proc relativeTo*(fullPath: AbsoluteFile, baseFilename: AbsoluteDir;
                   sep = DirSep): RelativeFile =
    # this currently fails for `tests/compilerapi/tcompilerapi.nim`
    # it's needed otherwise would returns an absolute path
    # assert not baseFilename.isEmpty, $fullPath
    result = RelativeFile(relativePath(fullPath.string, baseFilename.string, sep))

  proc toAbsolute*(file: string; base: AbsoluteDir): AbsoluteFile =
    if isAbsolute(file): result = AbsoluteFile(file)
    else: result = base / RelativeFile file

  proc changeFileExt*(x: AbsoluteFile; ext: string): AbsoluteFile {.borrow.}
  proc changeFileExt*(x: RelativeFile; ext: string): RelativeFile {.borrow.}

  proc addFileExt*(x: AbsoluteFile; ext: string): AbsoluteFile {.borrow.}
  proc addFileExt*(x: RelativeFile; ext: string): RelativeFile {.borrow.}

  proc writeFile*(x: AbsoluteFile; content: string) {.borrow.}

proc skipHomeDir(x: string): int =
  when defined(windows):
    if x.continuesWith("Users/", len("C:/")):
      result = 3
    else:
      result = 0
  else:
    if x.startsWith("/home/") or x.startsWith("/Users/"):
      result = 3
    elif x.startsWith("/mnt/") and x.continuesWith("/Users/", len("/mnt/c")):
      result = 5
    else:
      result = 0

proc relevantPart(s: string; afterSlashX: int): string =
  result = newStringOfCap(s.len - 8)
  var slashes = afterSlashX
  for i in 0..<s.len:
    if slashes == 0:
      result.add s[i]
    elif s[i] == '/':
      dec slashes

template canonSlashes(x: string): string =
  when defined(windows):
    x.replace('\\', '/')
  else:
    x

proc customPathImpl(x: string): string =
  # Idea: Encode a "protocol" via "//protocol/path" which is not ambiguous
  # as path canonicalization would have removed the double slashes.
  # /mnt/X/Users/Y
  # X:\\Users\Y
  # /home/Y
  # -->
  # //user/
  if not isAbsolute(x):
    result = customPathImpl(canonSlashes(getCurrentDir() / x))
  else:
    let slashes = skipHomeDir(x)
    if slashes > 0:
      result = "//user/" & relevantPart(x, slashes)
    else:
      result = x

proc customPath*(x: string): string =
  customPathImpl canonSlashes x