Skip to content

Commit

Permalink
add typesafe std/paths, std/files, std/dirs, std/symlinks (ni…
Browse files Browse the repository at this point in the history
…m-lang#20582)

* split std/os; add typesafe std/paths
* add more files, dirs, paths
* add documentation
* add testcase
* remove tryRemoveFile
* clean up
* Delete test.nim
* apply changes
* add `add` and fixes
  • Loading branch information
ringabout authored and capocasa committed Mar 31, 2023
1 parent 6a66bf3 commit 7022ac8
Show file tree
Hide file tree
Showing 6 changed files with 775 additions and 0 deletions.
177 changes: 177 additions & 0 deletions lib/std/dirs.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
from paths import Path, ReadDirEffect, WriteDirEffect

from std/private/osdirs import dirExists, createDir, existsOrCreateDir, removeDir,
moveDir, walkPattern, walkFiles, walkDirs, walkDir,
walkDirRec, PathComponent

export PathComponent

proc dirExists*(dir: Path): bool {.inline, tags: [ReadDirEffect].} =
## Returns true if the directory `dir` exists. If `dir` is a file, false
## is returned. Follows symlinks.
result = dirExists(dir.string)

proc createDir*(dir: Path) {.inline, tags: [WriteDirEffect, ReadDirEffect].} =
## Creates the `directory`:idx: `dir`.
##
## The directory may contain several subdirectories that do not exist yet.
## The full path is created. If this fails, `OSError` is raised.
##
## It does **not** fail if the directory already exists because for
## most usages this does not indicate an error.
##
## See also:
## * `removeDir proc`_
## * `existsOrCreateDir proc`_
## * `copyDir proc`_
## * `copyDirWithPermissions proc`_
## * `moveDir proc`_
createDir(dir.string)

proc existsOrCreateDir*(dir: Path): bool {.inline, tags: [WriteDirEffect, ReadDirEffect].} =
## Checks if a `directory`:idx: `dir` exists, and creates it otherwise.
##
## Does not create parent directories (raises `OSError` if parent directories do not exist).
## Returns `true` if the directory already exists, and `false` otherwise.
##
## See also:
## * `removeDir proc`_
## * `createDir proc`_
## * `copyDir proc`_
## * `copyDirWithPermissions proc`_
## * `moveDir proc`_
result = existsOrCreateDir(dir.string)

proc removeDir*(dir: Path, checkDir = false
) {.inline, tags: [WriteDirEffect, ReadDirEffect].} =
## Removes the directory `dir` including all subdirectories and files
## in `dir` (recursively).
##
## If this fails, `OSError` is raised. This does not fail if the directory never
## existed in the first place, unless `checkDir` = true.
##
## See also:
## * `removeFile proc`_
## * `existsOrCreateDir proc`_
## * `createDir proc`_
## * `copyDir proc`_
## * `copyDirWithPermissions proc`_
## * `moveDir proc`_
removeDir(dir.string, checkDir)

proc moveDir*(source, dest: Path) {.inline, tags: [ReadIOEffect, WriteIOEffect].} =
## Moves a directory from `source` to `dest`.
##
## Symlinks are not followed: if `source` contains symlinks, they themself are
## moved, not their target.
##
## If this fails, `OSError` is raised.
##
## See also:
## * `moveFile proc`_
## * `copyDir proc`_
## * `copyDirWithPermissions proc`_
## * `removeDir proc`_
## * `existsOrCreateDir proc`_
## * `createDir proc`_
moveDir(source.string, dest.string)

iterator walkPattern*(pattern: Path): Path {.tags: [ReadDirEffect].} =
## Iterate over all the files and directories that match the `pattern`.
##
## On POSIX this uses the `glob`:idx: call.
## `pattern` is OS dependent, but at least the `"*.ext"`
## notation is supported.
##
## See also:
## * `walkFiles iterator`_
## * `walkDirs iterator`_
## * `walkDir iterator`_
## * `walkDirRec iterator`_
for p in walkPattern(pattern.string):
yield Path(p)

iterator walkFiles*(pattern: Path): Path {.tags: [ReadDirEffect].} =
## Iterate over all the files that match the `pattern`.
##
## On POSIX this uses the `glob`:idx: call.
## `pattern` is OS dependent, but at least the `"*.ext"`
## notation is supported.
##
## See also:
## * `walkPattern iterator`_
## * `walkDirs iterator`_
## * `walkDir iterator`_
## * `walkDirRec iterator`_
for p in walkFiles(pattern.string):
yield Path(p)

iterator walkDirs*(pattern: Path): Path {.tags: [ReadDirEffect].} =
## Iterate over all the directories that match the `pattern`.
##
## On POSIX this uses the `glob`:idx: call.
## `pattern` is OS dependent, but at least the `"*.ext"`
## notation is supported.
##
## See also:
## * `walkPattern iterator`_
## * `walkFiles iterator`_
## * `walkDir iterator`_
## * `walkDirRec iterator`_
for p in walkDirs(pattern.string):
yield Path(p)

iterator walkDir*(dir: Path; relative = false, checkDir = false):
tuple[kind: PathComponent, path: Path] {.tags: [ReadDirEffect].} =
## Walks over the directory `dir` and yields for each directory or file in
## `dir`. The component type and full path for each item are returned.
##
## Walking is not recursive. If ``relative`` is true (default: false)
## the resulting path is shortened to be relative to ``dir``.
##
## If `checkDir` is true, `OSError` is raised when `dir`
## doesn't exist.
for (k, p) in walkDir(dir.string, relative, checkDir):
yield (k, Path(p))

iterator walkDirRec*(dir: Path,
yieldFilter = {pcFile}, followFilter = {pcDir},
relative = false, checkDir = false): Path {.tags: [ReadDirEffect].} =
## Recursively walks over the directory `dir` and yields for each file
## or directory in `dir`.
##
## If ``relative`` is true (default: false) the resulting path is
## shortened to be relative to ``dir``, otherwise the full path is returned.
##
## If `checkDir` is true, `OSError` is raised when `dir`
## doesn't exist.
##
## .. warning:: Modifying the directory structure while the iterator
## is traversing may result in undefined behavior!
##
## Walking is recursive. `followFilter` controls the behaviour of the iterator:
##
## ===================== =============================================
## yieldFilter meaning
## ===================== =============================================
## ``pcFile`` yield real files (default)
## ``pcLinkToFile`` yield symbolic links to files
## ``pcDir`` yield real directories
## ``pcLinkToDir`` yield symbolic links to directories
## ===================== =============================================
##
## ===================== =============================================
## followFilter meaning
## ===================== =============================================
## ``pcDir`` follow real directories (default)
## ``pcLinkToDir`` follow symbolic links to directories
## ===================== =============================================
##
##
## See also:
## * `walkPattern iterator`_
## * `walkFiles iterator`_
## * `walkDirs iterator`_
## * `walkDir iterator`_
for p in walkDirRec(dir.string, yieldFilter, followFilter, relative, checkDir):
yield Path(p)
45 changes: 45 additions & 0 deletions lib/std/files.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from paths import Path, ReadDirEffect, WriteDirEffect

from std/private/osfiles import fileExists, removeFile,
moveFile


proc fileExists*(filename: Path): bool {.inline, tags: [ReadDirEffect].} =
## Returns true if `filename` exists and is a regular file or symlink.
##
## Directories, device files, named pipes and sockets return false.
result = fileExists(filename.string)

proc removeFile*(file: Path) {.inline, tags: [WriteDirEffect].} =
## Removes the `file`.
##
## If this fails, `OSError` is raised. This does not fail
## if the file never existed in the first place.
##
## On Windows, ignores the read-only attribute.
##
## See also:
## * `removeDir proc`_
## * `copyFile proc`_
## * `copyFileWithPermissions proc`_
## * `moveFile proc`_
removeFile(file.string)

proc moveFile*(source, dest: Path) {.inline,
tags: [ReadDirEffect, ReadIOEffect, WriteIOEffect].} =
## Moves a file from `source` to `dest`.
##
## Symlinks are not followed: if `source` is a symlink, it is itself moved,
## not its target.
##
## If this fails, `OSError` is raised.
## If `dest` already exists, it will be overwritten.
##
## Can be used to `rename files`:idx:.
##
## See also:
## * `moveDir proc`_
## * `copyFile proc`_
## * `copyFileWithPermissions proc`_
## * `removeFile proc`_
moveFile(source.string, dest.string)
Loading

0 comments on commit 7022ac8

Please sign in to comment.