Compare commits
No commits in common. "master" and "0.9.1" have entirely different histories.
2
.ghci
Executable file
2
.ghci
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
:set -package HUnit -package hspec
|
||||||
|
:set -package template-haskell
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -8,7 +8,5 @@ TAGS
|
|||||||
tags
|
tags
|
||||||
*.tag
|
*.tag
|
||||||
.stack-work/
|
.stack-work/
|
||||||
dist/
|
|
||||||
dist-newstyle/
|
|
||||||
.cabal-sandbox/
|
.cabal-sandbox/
|
||||||
cabal.sandbox.config
|
cabal.sandbox.config
|
||||||
|
89
.travis.yml
89
.travis.yml
@ -7,76 +7,53 @@ dist: trusty
|
|||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- env: CABALVER=3.0 GHCVER=7.10.3 SKIP_DOCTESTS=yes
|
- env: CABALVER=1.22 GHCVER=7.8.4
|
||||||
addons: {apt: {packages: [cabal-install-3.0,ghc-7.10.3], sources: [hvr-ghc]}}
|
addons: {apt: {packages: [cabal-install-1.22,ghc-7.8.4], sources: [hvr-ghc]}}
|
||||||
before_install:
|
- env: CABALVER=1.24 GHCVER=7.10.2
|
||||||
- sudo apt-get install -y hscolour
|
addons: {apt: {packages: [cabal-install-1.24,ghc-7.10.2], sources: [hvr-ghc]}}
|
||||||
- export PATH=~/.cabal/bin:/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
|
- env: CABALVER=1.24 GHCVER=8.0.1
|
||||||
- env: CABALVER=3.0 GHCVER=8.0.2 SKIP_DOCTESTS=yes
|
addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.1], sources: [hvr-ghc]}}
|
||||||
addons: {apt: {packages: [cabal-install-3.0,ghc-8.0.2], sources: [hvr-ghc]}}
|
|
||||||
before_install:
|
|
||||||
- sudo apt-get install -y hscolour
|
|
||||||
- export PATH=~/.cabal/bin:/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
|
|
||||||
- env: CABALVER=3.0 GHCVER=8.2.2
|
|
||||||
addons: {apt: {packages: [cabal-install-3.0,ghc-8.2.2], sources: [hvr-ghc]}}
|
|
||||||
before_install:
|
|
||||||
- sudo apt-get install -y hscolour
|
|
||||||
- export PATH=~/.cabal/bin:/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
|
|
||||||
- env: CABALVER=3.0 GHCVER=8.4.4
|
|
||||||
addons: {apt: {packages: [cabal-install-3.0,ghc-8.4.4], sources: [hvr-ghc]}}
|
|
||||||
before_install:
|
|
||||||
- sudo apt-get install -y hscolour
|
|
||||||
- export PATH=~/.cabal/bin:/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
|
|
||||||
- env: CABALVER=3.0 GHCVER=8.6.5
|
|
||||||
addons: {apt: {packages: [cabal-install-3.0,ghc-8.6.5], sources: [hvr-ghc]}}
|
|
||||||
before_install:
|
|
||||||
- sudo apt-get install -y hscolour
|
|
||||||
- export PATH=~/.cabal/bin:/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
|
|
||||||
- env: CABALVER=3.0 GHCVER=8.8.3
|
|
||||||
addons: {apt: {packages: [cabal-install-3.0,ghc-8.8.3], sources: [hvr-ghc]}}
|
|
||||||
before_install:
|
|
||||||
- sudo apt-get install -y hscolour
|
|
||||||
- export PATH=~/.cabal/bin:/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
|
|
||||||
- env: CABALVER=head GHCVER=head
|
- env: CABALVER=head GHCVER=head
|
||||||
addons: {apt: {packages: [cabal-install-head,ghc-head], sources: [hvr-ghc]}}
|
addons: {apt: {packages: [cabal-install-head,ghc-head], sources: [hvr-ghc]}}
|
||||||
before_install:
|
|
||||||
- sudo apt-get install -y hscolour
|
|
||||||
- export PATH=~/.cabal/bin:/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
|
|
||||||
- os: osx
|
|
||||||
osx_image: xcode11.3
|
|
||||||
language: generic
|
|
||||||
before_install:
|
|
||||||
- mkdir -p ~/.ghcup/bin
|
|
||||||
- curl https://gitlab.haskell.org/haskell/ghcup/raw/master/ghcup > ~/.ghcup/bin/ghcup
|
|
||||||
- chmod +x ~/.ghcup/bin/ghcup
|
|
||||||
- export PATH=~/.cabal/bin:~/.ghcup/bin:$PATH
|
|
||||||
- ghcup install 8.6.5
|
|
||||||
- ghcup set 8.6.5
|
|
||||||
- ghcup install-cabal 3.2.0.0
|
|
||||||
|
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- env: CABALVER=head GHCVER=head
|
- env: CABALVER=head GHCVER=head
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- secure: HPBARvNM85ea2U0Ynq5MMe6BRlnuwqXWuSn20VY3EYCAT2njkVPYnR3O7+bGE6aq0KHAV87zz5iUfGJontd86tE0sDVjcSuRY0hqjOeJTkQq5M8WXJZOpVqlBTwDP1Q3x/fwoRa0dt9Z0tZZdKMlrf2XdcKPDdhcP1QYP4aV/jO4ZCfAQr7zVCvTae+Lp/KmwFYcBbFo/pj0duF1M4Oqx/D388b/W4jVE3lgd/TK7Ja1xWP6g+Oyvo6iQK8yJVYGdm6E+cVsNueiisnTJ/rRA53lsaC9dmWtZaFGl41wPviSU5zPq03vOuZMiyE2WtCHoo46ONXrXJ9N2soqdQVfEkr9Nw5LQl+6C5lCPEejZ575YUkuO05H3wvHMk3YY4zWXNFA9eZ47PEH8tpoUk9LPBacCKQFtp5lfRk63crba5CiFtcMyFq++0mLpNthNvtto7ffHMZrt6fvK9axI+r21VPftf/3FiFY4mnCp/Bln+ijklfZSN71VqiT20EWuqxQHw8aCpT00KA/PKGl9iJfoN4OO3XzNRTtmM+L9Im4bc1ni9YQ6N3UYg3z0nEnCLwFcTmTH/tDMHRremE0dM6B++YfcnyIhen8w+hG4bcXk7jbMUizRhUhStN7TZQuC9S4wE5whhp9c03rJZMmH5E2rlXY3lwVgeyWm1TuMp1RYWI=
|
- secure: q++z4DGwOHYjmed00oxMnGhBTzOBzKYunXvVcnCEmvmzW3qZERtXj3B7CLW4vRtmBlo3SiM0fb25NeYao+ByzTjo8jk9noiBVZvffwRmlKCeVwYx7T4/rsDhfV97k2JOeahBSgxWNuTkt+5gv07HpKdTiIxJsiv/QdBxQeq6/Ly6dyRskmCt+VuFvQg+cqPMugxIXtY6F7eZ1zgl/LxlamWjO3E4lX0Myf4o8+SU1HRDVkkVe+ytnRcVcYI2FHuFV/sSoDMTweXQToA9roVjOkfhq4rGlPCuXJkBPyZW2otLXgAV7I2kjwgxqmS5Yw752CcFjMMbG6R1u8sEAcGrJNKHfx8sKqBwI0AVoq4CJn+nKSElTDl0KI1mqazmazK4/mddkD9NGIVXCFmw4b+YGf1uDj8FAR94UmOiEFkEObGkQxG1XK/uzDaUJ1tO3MYXjPPEIE89BJORo+ZskmKFEoqbrBR/vEjbXxJHWP7SaaoM+mWpMiSssEFb/Z5mDBFPb2P/2f7nO4ZDfOYp/9hZdBvDaVM8FmTQfzF6jIUIOFmeeiSZWIBAHoDfdZDRrM/hC5JzqfMumW9frwllsQtYytkAsUqlNnCW86jlc5/5L6D8eY2NERFI2DRqrBi7bP2AfYXsozY0gMO1RL5+iQSQVKlPhk6IyAJYCWCYnrA+dz4=
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- sudo apt-get install -y hscolour
|
||||||
|
- export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- cabal --version
|
- cabal --version
|
||||||
- travis_retry cabal update
|
- travis_retry cabal update
|
||||||
- cabal install --installdir=$HOME/.cabal/bin hspec-discover
|
- cabal sandbox init
|
||||||
- cabal install --installdir=$HOME/.cabal/bin doctest
|
- cabal install --only-dependencies --enable-tests -j
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- cabal build --enable-tests all
|
- cabal configure --enable-tests -v2
|
||||||
- cabal test all
|
- cabal build
|
||||||
- ./hpath/run-doctests.sh
|
- cabal test
|
||||||
- ./hpath-filepath/run-doctests.sh
|
- cabal check
|
||||||
- (cd hpath && cabal check)
|
- cabal sdist
|
||||||
- (cd hpath-filepath && cabal check)
|
- cabal haddock --hyperlink-source --html-location=https://hackage.haskell.org/package/\$pkg-\$version/docs/
|
||||||
- (cd hpath-io && cabal check)
|
# check that the generated source-distribution can be built & installed
|
||||||
- cabal sdist all
|
- export SRC_TGZ=$(cabal info . | awk '{print $2 ".tar.gz";exit}') ;
|
||||||
- cabal install --lib all
|
cd dist/;
|
||||||
|
cabal sandbox init;
|
||||||
|
if [ -f "$SRC_TGZ" ]; then
|
||||||
|
cabal install "$SRC_TGZ" --enable-tests;
|
||||||
|
else
|
||||||
|
echo "expected '$SRC_TGZ' not found";
|
||||||
|
exit 1;
|
||||||
|
fi;
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
after_script:
|
||||||
|
- ./update-gh-pages.sh
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
email:
|
email:
|
||||||
|
@ -1,20 +1,3 @@
|
|||||||
0.11.0
|
|
||||||
* Many API breaking changes
|
|
||||||
* Remove RelC and Fn, because they complicate API/break semantics (see #29)
|
|
||||||
* Redo 'parseAny'
|
|
||||||
* Unexpose HPath.Internal
|
|
||||||
* Don't preserve trailing path separators (if you need to pass something to a C function that way, do it manually)
|
|
||||||
* Added `rooPath`, `isRootPath`, `getAllComponents`, `getAllComponentsAfterRoot`
|
|
||||||
0.10.2
|
|
||||||
* Add `parseAny` and the related QuasiQuoter
|
|
||||||
0.10.1
|
|
||||||
* Add quasi quoters for hpath
|
|
||||||
0.10.0
|
|
||||||
* split packages, this one now just contains the type-safe Path wrappers
|
|
||||||
0.9.2
|
|
||||||
* fix build with ghc-7.6
|
|
||||||
* raise required bytestring version
|
|
||||||
* Tighten base bound to prevent building before GHC 7.6 (by George Wilson)
|
|
||||||
0.9.1
|
0.9.1
|
||||||
* fix build with ghc-7.8 and 7.10
|
* fix build with ghc-7.8 and 7.10
|
||||||
0.9.0
|
0.9.0
|
92
README.md
92
README.md
@ -1,19 +1,87 @@
|
|||||||
# HPath libraries
|
# HPath
|
||||||
|
|
||||||
[![Gitter chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/hasufell/hpath?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://api.travis-ci.org/hasufell/hpath.png?branch=master)](http://travis-ci.org/hasufell/hpath)
|
[![Gitter chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/hasufell/hpath?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Hackage version](https://img.shields.io/hackage/v/hpath.svg?label=Hackage)](https://hackage.haskell.org/package/hpath) [![Build Status](https://api.travis-ci.org/hasufell/hpath.png?branch=master)](http://travis-ci.org/hasufell/hpath)
|
||||||
|
|
||||||
Set of libraries to deal with filepaths and files.
|
Support for well-typed paths in Haskell. Also provides ByteString based filepath
|
||||||
|
manipulation.
|
||||||
|
|
||||||
## Motivation
|
## Motivation
|
||||||
|
|
||||||
* filepaths should be type-safe (absolute, relative, ...)
|
The motivation came during development of
|
||||||
* filepaths should be ByteString under the hood, see [Abstract FilePath Proposal (AFPP)](https://gitlab.haskell.org/ghc/ghc/wikis/proposal/abstract-file-path)
|
[hsfm](https://github.com/hasufell/hsfm)
|
||||||
* file high-level operations should be platform-specific, exception-stable, safe and as atomic as possible
|
which has a pretty strict File type, but lacks a strict Path type, e.g.
|
||||||
|
for user input.
|
||||||
|
|
||||||
## Projects
|
The library that came closest to my needs was
|
||||||
|
[path](https://github.com/chrisdone/path),
|
||||||
|
but the API turned out to be oddly complicated for my use case, so I
|
||||||
|
decided to fork it.
|
||||||
|
|
||||||
|
Similarly, [posix-paths](https://github.com/JohnLato/posix-paths)
|
||||||
|
was exactly what I wanted for the low-level operations, but upstream seems dead,
|
||||||
|
so it is forked as well and merged into this library.
|
||||||
|
|
||||||
|
## Goals
|
||||||
|
|
||||||
|
* well-typed paths
|
||||||
|
* high-level API to file operations like recursive directory copy
|
||||||
|
* safe filepath manipulation, never using String as filepath, but ByteString
|
||||||
|
* still allowing sufficient control to interact with the underlying low-level calls
|
||||||
|
|
||||||
|
Note: this library was written for __posix__ systems and it will probably not support other systems.
|
||||||
|
|
||||||
|
## Differences to 'path'
|
||||||
|
|
||||||
|
* doesn't attempt to fake IO-related information into the path, so whether a path points to a file or directory is up to your IO-code to decide...
|
||||||
|
* trailing path separators will be preserved if they exist, no messing with that
|
||||||
|
* uses safe ByteString for filepaths under the hood instead of unsafe String
|
||||||
|
* fixes broken [dirname](https://github.com/chrisdone/path/issues/18)
|
||||||
|
* renames dirname/filename to basename/dirname to match the POSIX shell functions
|
||||||
|
* introduces a new `Path Fn` for safe filename guarantees and a `RelC` class
|
||||||
|
* allows pattern matching via unidirectional PatternSynonym
|
||||||
|
* uses simple doctest for testing
|
||||||
|
* allows `~/` as relative path, because on posix level `~` is just a regular filename that does _NOT_ point to `$HOME`
|
||||||
|
* remove TH, it sucks
|
||||||
|
|
||||||
|
## Differences to 'posix-paths'
|
||||||
|
|
||||||
|
* uses the `word8` package for save word8 literals instead of `OverloadedStrings`
|
||||||
|
* `hasTrailingPathSeparator` and `dropTrailingPathSeparator` behave in the same way as their `System.FilePath` counterpart
|
||||||
|
* added various functions:
|
||||||
|
* `equalFilePath`
|
||||||
|
* `getSearchPath`
|
||||||
|
* `hasParentDir`
|
||||||
|
* `hiddenFile`
|
||||||
|
* `isFileName`
|
||||||
|
* `isValid`
|
||||||
|
* `makeRelative`
|
||||||
|
* `makeValid`
|
||||||
|
* `normalise`
|
||||||
|
* `splitSearchPath`
|
||||||
|
* `stripExtension`
|
||||||
|
* has a custom versions of `openFd` which allows more control over the flags than its unix package counterpart
|
||||||
|
* adds a `getDirectoryContents'` version that works on Fd
|
||||||
|
|
||||||
|
## Examples in ghci
|
||||||
|
|
||||||
|
Start ghci via `cabal repl`:
|
||||||
|
|
||||||
|
```hs
|
||||||
|
-- enable OverloadedStrings
|
||||||
|
:set -XOverloadedStrings
|
||||||
|
-- import HPath.IO
|
||||||
|
import HPath.IO
|
||||||
|
-- parse an absolute path
|
||||||
|
abspath <- parseAbs "/home"
|
||||||
|
-- parse a relative path (e.g. user users home directory)
|
||||||
|
relpath <- parseRel "jule"
|
||||||
|
-- concatenate paths
|
||||||
|
let newpath = abspath </> relpath
|
||||||
|
-- get file type
|
||||||
|
getFileType newpath
|
||||||
|
-- return all contents of that directory
|
||||||
|
getDirsFiles newpath
|
||||||
|
-- return all contents of the parent directory
|
||||||
|
getDirsFiles (dirname newpath)
|
||||||
|
```
|
||||||
|
|
||||||
* [![Hackage version](https://img.shields.io/hackage/v/hpath.svg?label=Hackage)](https://hackage.haskell.org/package/hpath) [hpath](./hpath): Support for well-typed paths
|
|
||||||
* [![Hackage version](https://img.shields.io/hackage/v/hpath-filepath.svg?label=Hackage)](https://hackage.haskell.org/package/hpath-filepath) [hpath-filepath](./hpath-filepath): ByteString based filepath manipulation (can be used without hpath)
|
|
||||||
* [![Hackage version](https://img.shields.io/hackage/v/hpath-directory.svg?label=Hackage)](https://hackage.haskell.org/package/hpath-directory) [hpath-directory](./hpath-directory): High-level IO operations for files/directories on raw ByteString filepaths (use hpath-io for the type-safe path version)
|
|
||||||
* [![Hackage version](https://img.shields.io/hackage/v/hpath-io.svg?label=Hackage)](https://hackage.haskell.org/package/hpath-io) [hpath-io](./hpath-io): High-level IO operations for files/directories utilizing type-safe Path
|
|
||||||
* [![Hackage version](https://img.shields.io/hackage/v/hpath-posix.svg?label=Hackage)](https://hackage.haskell.org/package/hpath-posix) [hpath-posix](./hpath-posix): Some low-level POSIX glue code that is not in 'unix'
|
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
packages: ./hpath
|
|
||||||
./hpath-directory
|
|
||||||
./hpath-filepath
|
|
||||||
./hpath-io
|
|
||||||
./hpath-posix
|
|
||||||
|
|
||||||
package hpath-io
|
|
||||||
ghc-options: -O2 -fspec-constr-recursive=16 -fmax-worker-args=16
|
|
||||||
|
|
||||||
-- https://github.com/composewell/streamly/blob/master/docs/Build.md
|
|
||||||
package streamly
|
|
||||||
ghc-options: -O2 -fspec-constr-recursive=16 -fmax-worker-args=16
|
|
@ -1,7 +1,7 @@
|
|||||||
#include "dirutils.h"
|
#include "dirutils.h"
|
||||||
|
|
||||||
unsigned int
|
unsigned int
|
||||||
__posixdir_d_type(struct dirent* d)
|
__posixdir_d_type(struct dirent* d)
|
||||||
{
|
{
|
||||||
return(d -> d_type);
|
return(d -> d_type);
|
||||||
}
|
}
|
||||||
|
|
@ -7,9 +7,7 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
|
||||||
extern unsigned int
|
extern unsigned int
|
||||||
__posixdir_d_type(struct dirent* d)
|
__posixdir_d_type(struct dirent* d)
|
||||||
;
|
;
|
||||||
|
|
||||||
#endif
|
#endif
|
13
doctests-hpath.hs
Normal file
13
doctests-hpath.hs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
module Main where
|
||||||
|
|
||||||
|
|
||||||
|
import Test.DocTest
|
||||||
|
import Test.HUnit
|
||||||
|
|
||||||
|
main =
|
||||||
|
doctest
|
||||||
|
["-isrc"
|
||||||
|
, "-XOverloadedStrings"
|
||||||
|
, "src/HPath.hs"
|
||||||
|
]
|
||||||
|
|
25
doctests-posix.hs
Normal file
25
doctests-posix.hs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
module Main where
|
||||||
|
|
||||||
|
import Control.Applicative
|
||||||
|
import System.Posix.Directory.Traversals
|
||||||
|
|
||||||
|
import Test.DocTest
|
||||||
|
import Test.HUnit
|
||||||
|
|
||||||
|
main = do
|
||||||
|
doctest
|
||||||
|
[ "-isrc"
|
||||||
|
, "-XOverloadedStrings"
|
||||||
|
, "System.Posix.FilePath"
|
||||||
|
]
|
||||||
|
runTestTT unitTests
|
||||||
|
|
||||||
|
|
||||||
|
unitTests :: Test
|
||||||
|
unitTests = test
|
||||||
|
[ TestCase $ do
|
||||||
|
r <- (==) <$> allDirectoryContents "." <*> allDirectoryContents' "."
|
||||||
|
assertBool "allDirectoryContents == allDirectoryContents'" r
|
||||||
|
]
|
@ -1,21 +0,0 @@
|
|||||||
# Revision history for hpath-directory
|
|
||||||
|
|
||||||
## 0.13.4 -- 2020-05-08
|
|
||||||
|
|
||||||
* Add getDirsFilesStream and use streamly-posix for dircontents (#34)
|
|
||||||
|
|
||||||
## 0.13.3 -- 2020-04-14
|
|
||||||
|
|
||||||
* Fix tests on mac
|
|
||||||
|
|
||||||
## 0.13.2 -- 2020-02-17
|
|
||||||
|
|
||||||
* Fix bug in `createDirRecursive` with trailing path separators
|
|
||||||
|
|
||||||
## 0.13.1 -- 2020-01-29
|
|
||||||
|
|
||||||
* Split some functionality out into 'hpath-posix'
|
|
||||||
|
|
||||||
## 0.1.0.0 -- 2020-01-26
|
|
||||||
|
|
||||||
* First version. Released on an unsuspecting world.
|
|
@ -1,30 +0,0 @@
|
|||||||
Copyright (c) 2020, Julian Ospald
|
|
||||||
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following
|
|
||||||
disclaimer in the documentation and/or other materials provided
|
|
||||||
with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of Julian Ospald nor the names of other
|
|
||||||
contributors may be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,21 +0,0 @@
|
|||||||
# HPath-filepath
|
|
||||||
|
|
||||||
[![Gitter chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/hasufell/hpath?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Hackage version](https://img.shields.io/hackage/v/hpath-directory.svg?label=Hackage)](https://hackage.haskell.org/package/hpath-directory) [![Build Status](https://api.travis-ci.org/hasufell/hpath.png?branch=master)](http://travis-ci.org/hasufell/hpath) [![Hackage-Deps](https://img.shields.io/hackage-deps/v/hpath-directory.svg)](http://packdeps.haskellers.com/feed?needle=hpath-directory)
|
|
||||||
|
|
||||||
Support high-level IO operations on files/directories, utilizing ByteString
|
|
||||||
as FilePaths.
|
|
||||||
|
|
||||||
This package is part of the HPath suite, also check out:
|
|
||||||
|
|
||||||
* [hpath](https://hackage.haskell.org/package/hpath)
|
|
||||||
* [hpath-filepath](https://hackage.haskell.org/package/hpath-filepath)
|
|
||||||
* [hpath-io](https://hackage.haskell.org/package/hpath-io)
|
|
||||||
|
|
||||||
## Motivation
|
|
||||||
|
|
||||||
This is basically a fork of [directory](https://hackage.haskell.org/package/directory), but is a complete rewrite and the API doesn't follow the directory package.
|
|
||||||
|
|
||||||
## Differences to 'posix-paths'
|
|
||||||
|
|
||||||
* has a custom versions of `openFd` which allows more control over the flags than its unix package counterpart
|
|
||||||
* adds a `getDirectoryContents'` version that works on Fd
|
|
@ -1,116 +0,0 @@
|
|||||||
cabal-version: >=1.10
|
|
||||||
|
|
||||||
name: hpath-directory
|
|
||||||
version: 0.13.4
|
|
||||||
synopsis: Alternative to 'directory' package with ByteString based filepaths
|
|
||||||
description: This provides a safer alternative to the 'directory'
|
|
||||||
package. FilePaths are ByteString based, so this
|
|
||||||
package only works on POSIX systems.
|
|
||||||
|
|
||||||
For a more high-level version of this with
|
|
||||||
proper Path type, use 'hpath-io', which makes
|
|
||||||
use of this package.
|
|
||||||
homepage: https://github.com/hasufell/hpath
|
|
||||||
bug-reports: https://github.com/hasufell/hpath/issues
|
|
||||||
license: BSD3
|
|
||||||
license-file: LICENSE
|
|
||||||
author: Julian Ospald <hasufell@posteo.de>
|
|
||||||
maintainer: Julian Ospald <hasufell@posteo.de>
|
|
||||||
copyright: Julian Ospald <hasufell@posteo.de> 2020
|
|
||||||
category: Filesystem
|
|
||||||
build-type: Simple
|
|
||||||
extra-source-files: CHANGELOG.md
|
|
||||||
tested-with: GHC==7.10.3
|
|
||||||
, GHC==8.0.2
|
|
||||||
, GHC==8.2.2
|
|
||||||
, GHC==8.4.4
|
|
||||||
, GHC==8.6.5
|
|
||||||
, GHC==8.8.1
|
|
||||||
|
|
||||||
library
|
|
||||||
if os(windows)
|
|
||||||
build-depends: unbuildable<0
|
|
||||||
buildable: False
|
|
||||||
exposed-modules: System.Posix.RawFilePath.Directory
|
|
||||||
System.Posix.RawFilePath.Directory.Errors
|
|
||||||
-- other-modules:
|
|
||||||
-- other-extensions:
|
|
||||||
build-depends: base >= 4.8 && <5
|
|
||||||
, IfElse
|
|
||||||
, bytestring >= 0.10
|
|
||||||
, exceptions >= 0.10
|
|
||||||
, hpath-filepath >= 0.10.3
|
|
||||||
, hpath-posix >= 0.13
|
|
||||||
, safe-exceptions >= 0.1
|
|
||||||
, streamly >= 0.7
|
|
||||||
, streamly-bytestring >= 0.1.2
|
|
||||||
, streamly-posix >= 0.1.0.1
|
|
||||||
, time >= 1.8
|
|
||||||
, transformers
|
|
||||||
, unix >= 2.5
|
|
||||||
, unix-bytestring >= 0.3
|
|
||||||
, utf8-string
|
|
||||||
if impl(ghc < 8.0)
|
|
||||||
build-depends:
|
|
||||||
fail >= 4.9
|
|
||||||
|
|
||||||
hs-source-dirs: src
|
|
||||||
default-language: Haskell2010
|
|
||||||
default-extensions: PackageImports
|
|
||||||
|
|
||||||
test-suite spec
|
|
||||||
if os(windows)
|
|
||||||
build-depends: unbuildable<0
|
|
||||||
buildable: False
|
|
||||||
Type: exitcode-stdio-1.0
|
|
||||||
Default-Language: Haskell2010
|
|
||||||
Hs-Source-Dirs: test
|
|
||||||
Main-Is: Main.hs
|
|
||||||
other-modules:
|
|
||||||
System.Posix.RawFilePath.Directory.AppendFileSpec
|
|
||||||
System.Posix.RawFilePath.Directory.CanonicalizePathSpec
|
|
||||||
System.Posix.RawFilePath.Directory.CopyDirRecursiveCollectFailuresSpec
|
|
||||||
System.Posix.RawFilePath.Directory.CopyDirRecursiveOverwriteSpec
|
|
||||||
System.Posix.RawFilePath.Directory.CopyDirRecursiveSpec
|
|
||||||
System.Posix.RawFilePath.Directory.CopyFileOverwriteSpec
|
|
||||||
System.Posix.RawFilePath.Directory.CopyFileSpec
|
|
||||||
System.Posix.RawFilePath.Directory.CreateDirIfMissingSpec
|
|
||||||
System.Posix.RawFilePath.Directory.CreateDirRecursiveSpec
|
|
||||||
System.Posix.RawFilePath.Directory.CreateDirSpec
|
|
||||||
System.Posix.RawFilePath.Directory.CreateRegularFileSpec
|
|
||||||
System.Posix.RawFilePath.Directory.CreateSymlinkSpec
|
|
||||||
System.Posix.RawFilePath.Directory.DeleteDirRecursiveSpec
|
|
||||||
System.Posix.RawFilePath.Directory.DeleteDirSpec
|
|
||||||
System.Posix.RawFilePath.Directory.DeleteFileSpec
|
|
||||||
System.Posix.RawFilePath.Directory.GetDirsFilesSpec
|
|
||||||
System.Posix.RawFilePath.Directory.GetFileTypeSpec
|
|
||||||
System.Posix.RawFilePath.Directory.MoveFileOverwriteSpec
|
|
||||||
System.Posix.RawFilePath.Directory.MoveFileSpec
|
|
||||||
System.Posix.RawFilePath.Directory.ReadFileSpec
|
|
||||||
System.Posix.RawFilePath.Directory.RecreateSymlinkOverwriteSpec
|
|
||||||
System.Posix.RawFilePath.Directory.RecreateSymlinkSpec
|
|
||||||
System.Posix.RawFilePath.Directory.RenameFileSpec
|
|
||||||
System.Posix.RawFilePath.Directory.ToAbsSpec
|
|
||||||
System.Posix.RawFilePath.Directory.WriteFileLSpec
|
|
||||||
System.Posix.RawFilePath.Directory.WriteFileSpec
|
|
||||||
Spec
|
|
||||||
Utils
|
|
||||||
GHC-Options: -Wall
|
|
||||||
Build-Depends: base
|
|
||||||
, HUnit
|
|
||||||
, IfElse
|
|
||||||
, bytestring >= 0.10.0.0
|
|
||||||
, hpath-directory
|
|
||||||
, hpath-filepath >= 0.10
|
|
||||||
, hpath-posix >= 0.13
|
|
||||||
, hspec >= 1.3
|
|
||||||
, process
|
|
||||||
, time >= 1.8
|
|
||||||
, unix
|
|
||||||
, unix-bytestring
|
|
||||||
, utf8-string
|
|
||||||
default-extensions: PackageImports
|
|
||||||
|
|
||||||
source-repository head
|
|
||||||
type: git
|
|
||||||
location: https://github.com/hasufell/hpath
|
|
File diff suppressed because it is too large
Load Diff
@ -1,15 +0,0 @@
|
|||||||
module System.Posix.RawFilePath.Directory where
|
|
||||||
|
|
||||||
import System.Posix.ByteString.FilePath (RawFilePath)
|
|
||||||
|
|
||||||
canonicalizePath :: RawFilePath -> IO RawFilePath
|
|
||||||
|
|
||||||
toAbs :: RawFilePath -> IO RawFilePath
|
|
||||||
|
|
||||||
doesFileExist :: RawFilePath -> IO Bool
|
|
||||||
|
|
||||||
doesDirectoryExist :: RawFilePath -> IO Bool
|
|
||||||
|
|
||||||
isWritable :: RawFilePath -> IO Bool
|
|
||||||
|
|
||||||
canOpenDirectory :: RawFilePath -> IO Bool
|
|
@ -1,28 +0,0 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
|
||||||
|
|
||||||
import qualified Data.ByteString as BS
|
|
||||||
import Data.IORef
|
|
||||||
import Test.Hspec
|
|
||||||
import Test.Hspec.Runner
|
|
||||||
import Test.Hspec.Formatters
|
|
||||||
import qualified Spec
|
|
||||||
import Utils
|
|
||||||
import System.Posix.Temp.ByteString (mkdtemp)
|
|
||||||
import System.Posix.Env.ByteString (getEnvDefault)
|
|
||||||
import System.Posix.FilePath ((</>))
|
|
||||||
import "hpath-directory" System.Posix.RawFilePath.Directory
|
|
||||||
|
|
||||||
|
|
||||||
-- TODO: chardev, blockdev, namedpipe, socket
|
|
||||||
|
|
||||||
|
|
||||||
main :: IO ()
|
|
||||||
main = do
|
|
||||||
tmpdir <- getEnvDefault "TMPDIR" "/tmp" >>= canonicalizePath
|
|
||||||
tmpBase <- mkdtemp (tmpdir </> "hpath-directory")
|
|
||||||
writeIORef baseTmpDir (Just (tmpBase `BS.append` "/"))
|
|
||||||
putStrLn $ ("Temporary test directory at: " ++ show tmpBase)
|
|
||||||
hspecWith
|
|
||||||
defaultConfig { configFormatter = Just progress }
|
|
||||||
$ afterAll_ deleteBaseTmpDir
|
|
||||||
$ Spec.spec
|
|
@ -1,69 +0,0 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.CreateDirIfMissingSpec where
|
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
|
||||||
import System.IO.Error
|
|
||||||
(
|
|
||||||
ioeGetErrorType
|
|
||||||
)
|
|
||||||
import GHC.IO.Exception
|
|
||||||
(
|
|
||||||
IOErrorType(..)
|
|
||||||
)
|
|
||||||
import Utils
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
upTmpDir :: IO ()
|
|
||||||
upTmpDir = do
|
|
||||||
setTmpDir "CreateDirIfMissingSpec"
|
|
||||||
createTmpDir
|
|
||||||
|
|
||||||
setupFiles :: IO ()
|
|
||||||
setupFiles = do
|
|
||||||
createDir' "alreadyExists"
|
|
||||||
createDir' "noPerms"
|
|
||||||
createDir' "noWritePerms"
|
|
||||||
noPerms "noPerms"
|
|
||||||
noWritableDirPerms "noWritePerms"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
cleanupFiles :: IO ()
|
|
||||||
cleanupFiles = do
|
|
||||||
normalDirPerms "noPerms"
|
|
||||||
normalDirPerms "noWritePerms"
|
|
||||||
deleteDir' "alreadyExists"
|
|
||||||
deleteDir' "noPerms"
|
|
||||||
deleteDir' "noWritePerms"
|
|
||||||
|
|
||||||
|
|
||||||
spec :: Spec
|
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
|
||||||
describe "System.Posix.RawFilePath.Directory.CreateDirIfMissing" $ do
|
|
||||||
|
|
||||||
-- successes --
|
|
||||||
it "createDirIfMissing, all fine" $ do
|
|
||||||
createDirIfMissing' "newDir"
|
|
||||||
removeDirIfExists "newDir"
|
|
||||||
|
|
||||||
it "createDirIfMissing, destination directory already exists" $
|
|
||||||
createDirIfMissing' "alreadyExists"
|
|
||||||
|
|
||||||
-- posix failures --
|
|
||||||
it "createDirIfMissing, parent directories do not exist" $
|
|
||||||
createDirIfMissing' "some/thing/dada"
|
|
||||||
`shouldThrow`
|
|
||||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
|
||||||
|
|
||||||
it "createDirIfMissing, can't write to output directory" $
|
|
||||||
createDirIfMissing' "noWritePerms/newDir"
|
|
||||||
`shouldThrow`
|
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
|
||||||
|
|
||||||
it "createDirIfMissing, can't open output directory" $
|
|
||||||
createDirIfMissing' "noPerms/newDir"
|
|
||||||
`shouldThrow`
|
|
||||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
|
@ -1,108 +0,0 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
|
||||||
|
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.WriteFileLSpec where
|
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
|
||||||
import System.IO.Error
|
|
||||||
(
|
|
||||||
ioeGetErrorType
|
|
||||||
)
|
|
||||||
import GHC.IO.Exception
|
|
||||||
(
|
|
||||||
IOErrorType(..)
|
|
||||||
)
|
|
||||||
import Utils
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
upTmpDir :: IO ()
|
|
||||||
upTmpDir = do
|
|
||||||
setTmpDir "WriteFileLSpec"
|
|
||||||
createTmpDir
|
|
||||||
|
|
||||||
setupFiles :: IO ()
|
|
||||||
setupFiles = do
|
|
||||||
createRegularFile' "fileWithContent"
|
|
||||||
createRegularFile' "fileWithoutContent"
|
|
||||||
createSymlink' "inputFileSymL" "fileWithContent"
|
|
||||||
createDir' "alreadyExistsD"
|
|
||||||
createRegularFile' "noPerms"
|
|
||||||
noPerms "noPerms"
|
|
||||||
createDir' "noPermsD"
|
|
||||||
createRegularFile' "noPermsD/inputFile"
|
|
||||||
noPerms "noPermsD"
|
|
||||||
writeFile' "fileWithContent" "BLKASL"
|
|
||||||
|
|
||||||
|
|
||||||
cleanupFiles :: IO ()
|
|
||||||
cleanupFiles = do
|
|
||||||
deleteFile' "fileWithContent"
|
|
||||||
deleteFile' "fileWithoutContent"
|
|
||||||
deleteFile' "inputFileSymL"
|
|
||||||
deleteDir' "alreadyExistsD"
|
|
||||||
normalFilePerms "noPerms"
|
|
||||||
deleteFile' "noPerms"
|
|
||||||
normalDirPerms "noPermsD"
|
|
||||||
deleteFile' "noPermsD/inputFile"
|
|
||||||
deleteDir' "noPermsD"
|
|
||||||
|
|
||||||
|
|
||||||
spec :: Spec
|
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
|
||||||
describe "System.Posix.RawFilePath.Directory.WriteFileL" $ do
|
|
||||||
|
|
||||||
-- successes --
|
|
||||||
it "WriteFileL file with content, everything clear" $ do
|
|
||||||
writeFileL' "fileWithContent" "blahfaselllll"
|
|
||||||
out <- readFile' "fileWithContent"
|
|
||||||
out `shouldBe` "blahfaselllll"
|
|
||||||
|
|
||||||
it "WriteFileL file with content, everything clear" $ do
|
|
||||||
writeFileL' "fileWithContent" "gagagaga"
|
|
||||||
out <- readFile' "fileWithContent"
|
|
||||||
out `shouldBe` "gagagaga"
|
|
||||||
|
|
||||||
it "WriteFileL file with content, everything clear" $ do
|
|
||||||
writeFileL' "fileWithContent" ""
|
|
||||||
out <- readFile' "fileWithContent"
|
|
||||||
out `shouldBe` ""
|
|
||||||
|
|
||||||
it "WriteFileL file without content, everything clear" $ do
|
|
||||||
writeFileL' "fileWithoutContent" "blahfaselllll"
|
|
||||||
out <- readFile' "fileWithoutContent"
|
|
||||||
out `shouldBe` "blahfaselllll"
|
|
||||||
|
|
||||||
it "WriteFileL, everything clear" $ do
|
|
||||||
writeFileL' "fileWithoutContent" "gagagaga"
|
|
||||||
out <- readFile' "fileWithoutContent"
|
|
||||||
out `shouldBe` "gagagaga"
|
|
||||||
|
|
||||||
it "WriteFileL symlink, everything clear" $ do
|
|
||||||
writeFileL' "inputFileSymL" "blahfaselllll"
|
|
||||||
out <- readFile' "inputFileSymL"
|
|
||||||
out `shouldBe` "blahfaselllll"
|
|
||||||
|
|
||||||
it "WriteFileL symlink, everything clear" $ do
|
|
||||||
writeFileL' "inputFileSymL" "gagagaga"
|
|
||||||
out <- readFile' "inputFileSymL"
|
|
||||||
out `shouldBe` "gagagaga"
|
|
||||||
|
|
||||||
|
|
||||||
-- posix failures --
|
|
||||||
it "WriteFileL to dir, inappropriate type" $ do
|
|
||||||
writeFileL' "alreadyExistsD" ""
|
|
||||||
`shouldThrow` (\e -> ioeGetErrorType e == InappropriateType)
|
|
||||||
|
|
||||||
it "WriteFileL, no permissions to file" $ do
|
|
||||||
writeFileL' "noPerms" ""
|
|
||||||
`shouldThrow` (\e -> ioeGetErrorType e == PermissionDenied)
|
|
||||||
|
|
||||||
it "WriteFileL, no permissions to file" $ do
|
|
||||||
writeFileL' "noPermsD/inputFile" ""
|
|
||||||
`shouldThrow` (\e -> ioeGetErrorType e == PermissionDenied)
|
|
||||||
|
|
||||||
it "WriteFileL, file does not exist" $ do
|
|
||||||
writeFileL' "gaga" ""
|
|
||||||
`shouldThrow` (\e -> ioeGetErrorType e == NoSuchThing)
|
|
@ -1,14 +0,0 @@
|
|||||||
# Revision history for hpath-filepath
|
|
||||||
|
|
||||||
## 0.10.4 -- 2020-01-26
|
|
||||||
|
|
||||||
* Add `takeAllParents`
|
|
||||||
|
|
||||||
|
|
||||||
## 0.10.2 -- 2020-01-18
|
|
||||||
|
|
||||||
* Add `isSpecialDirectoryEntry`
|
|
||||||
|
|
||||||
## 0.10.0 -- 2020-01-04
|
|
||||||
|
|
||||||
* First version. Split from 'hpath', contains only the filepath ByteString manipulation parts.
|
|
@ -1,30 +0,0 @@
|
|||||||
Copyright (c) 2020, Julian Ospald
|
|
||||||
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following
|
|
||||||
disclaimer in the documentation and/or other materials provided
|
|
||||||
with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of Julian Ospald nor the names of other
|
|
||||||
contributors may be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,29 +0,0 @@
|
|||||||
# HPath-filepath
|
|
||||||
|
|
||||||
[![Gitter chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/hasufell/hpath?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Hackage version](https://img.shields.io/hackage/v/hpath-filepath.svg?label=Hackage)](https://hackage.haskell.org/package/hpath-filepath) [![Build Status](https://api.travis-ci.org/hasufell/hpath.png?branch=master)](http://travis-ci.org/hasufell/hpath) [![Hackage-Deps](https://img.shields.io/hackage-deps/v/hpath-filepath.svg)](http://packdeps.haskellers.com/feed?needle=hpath-filepath)
|
|
||||||
|
|
||||||
Support for bytestring based filepath manipulation, similar to 'filepath'.
|
|
||||||
|
|
||||||
This package is part of the HPath suite, also check out:
|
|
||||||
|
|
||||||
* [hpath](https://hackage.haskell.org/package/hpath)
|
|
||||||
* [hpath-directory](https://hackage.haskell.org/package/hpath-directory)
|
|
||||||
* [hpath-io](https://hackage.haskell.org/package/hpath-io)
|
|
||||||
|
|
||||||
## Motivation
|
|
||||||
|
|
||||||
This is basically a fork of [posix-paths](https://github.com/JohnLato/posix-paths), which seemed to have stalled development.
|
|
||||||
|
|
||||||
There is also a similar library [filepath-bytestring](https://hackage.haskell.org/package/filepath-bytestring), but it doesn't follow an open development model and is cross-platform, which this library is not interested in.
|
|
||||||
|
|
||||||
## Differences to 'posix-paths'
|
|
||||||
|
|
||||||
* uses the `word8` package for save word8 literals instead of `OverloadedStrings`
|
|
||||||
* `hasTrailingPathSeparator` and `dropTrailingPathSeparator` behave in the same way as their `System.FilePath` counterpart
|
|
||||||
* has some additional functions
|
|
||||||
|
|
||||||
## Differences to 'filepath-bytestring'
|
|
||||||
|
|
||||||
* uses the `word8` package for save word8 literals instead of `OverloadedStrings`
|
|
||||||
* is not cross-platform (less odd code to maintain)
|
|
||||||
* has some additional functions
|
|
@ -1,2 +0,0 @@
|
|||||||
import Distribution.Simple
|
|
||||||
main = defaultMain
|
|
@ -1,39 +0,0 @@
|
|||||||
name: hpath-filepath
|
|
||||||
version: 0.10.4
|
|
||||||
synopsis: ByteString based filepath manipulation
|
|
||||||
description: ByteString based filepath manipulation, similar to 'filepath' package. This is POSIX only.
|
|
||||||
-- bug-reports:
|
|
||||||
license: BSD3
|
|
||||||
license-file: LICENSE
|
|
||||||
author: Julian Ospald <hasufell@posteo.de>
|
|
||||||
maintainer: Julian Ospald <hasufell@posteo.de>
|
|
||||||
copyright: Julian Ospald 2016
|
|
||||||
category: Filesystem
|
|
||||||
build-type: Simple
|
|
||||||
cabal-version: 1.14
|
|
||||||
tested-with: GHC==7.10.3
|
|
||||||
, GHC==8.0.2
|
|
||||||
, GHC==8.2.2
|
|
||||||
, GHC==8.4.4
|
|
||||||
, GHC==8.6.5
|
|
||||||
, GHC==8.8.1
|
|
||||||
extra-source-files: README.md
|
|
||||||
CHANGELOG.md
|
|
||||||
|
|
||||||
library
|
|
||||||
if os(windows)
|
|
||||||
build-depends: unbuildable<0
|
|
||||||
buildable: False
|
|
||||||
exposed-modules: System.Posix.FilePath
|
|
||||||
-- other-modules:
|
|
||||||
-- other-extensions:
|
|
||||||
build-depends: base >=4.8 && <5
|
|
||||||
, bytestring >= 0.10.0.0
|
|
||||||
, unix >= 2.5
|
|
||||||
, word8
|
|
||||||
hs-source-dirs: src
|
|
||||||
default-language: Haskell2010
|
|
||||||
|
|
||||||
source-repository head
|
|
||||||
type: git
|
|
||||||
location: https://github.com/hasufell/hpath
|
|
@ -1,23 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
if [ -n "${SKIP_DOCTESTS}" ] ; then
|
|
||||||
echo "Skipping doctests"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v doctest >/dev/null ; then
|
|
||||||
tempdir="$(mktemp -d)"
|
|
||||||
(
|
|
||||||
cd "${tempdir}"
|
|
||||||
cabal install --installdir="${tempdir}" doctest
|
|
||||||
)
|
|
||||||
export PATH="${tempdir}:$PATH"
|
|
||||||
fi
|
|
||||||
|
|
||||||
set -x
|
|
||||||
|
|
||||||
cd "$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)"
|
|
||||||
|
|
||||||
cabal exec doctest -- -isrc -XOverloadedStrings System.Posix.FilePath
|
|
@ -1,47 +0,0 @@
|
|||||||
# Revision history for hpath-io
|
|
||||||
|
|
||||||
## 0.13.2 -- 2020-05-08
|
|
||||||
|
|
||||||
* Add getDirsFilesStream and use streamly-posix for dircontents (#34)
|
|
||||||
|
|
||||||
## 0.13.0 -- 2020-01-26
|
|
||||||
|
|
||||||
* switch to using 'hpath-bytestring' for the implementation (this is now just a wrapper module, mostly)
|
|
||||||
|
|
||||||
## 0.12.0 -- 2020-01-20
|
|
||||||
|
|
||||||
* breaking API changes
|
|
||||||
* RelC and Fn were removed from `hpath`
|
|
||||||
* further changes to `parseAny`
|
|
||||||
|
|
||||||
|
|
||||||
## 0.11.0 -- 2020-01-18
|
|
||||||
|
|
||||||
* `writeFile` not allows to set file mode and create file if it does not exist (this broke API)
|
|
||||||
* added various new functions:
|
|
||||||
* createDirIfMissing
|
|
||||||
* writeFileL (for lazy bytestring)
|
|
||||||
* isReadable
|
|
||||||
* isExecutable
|
|
||||||
* getModificationTime
|
|
||||||
* setModificationTime
|
|
||||||
* setModificationTimeHiRes
|
|
||||||
* getDirsFiles' (returns filenames instead of paths)
|
|
||||||
* withRawFilePath
|
|
||||||
* withHandle
|
|
||||||
|
|
||||||
## 0.10.1 -- 2020-01-13
|
|
||||||
|
|
||||||
* Move file check functions to HPath.IO
|
|
||||||
* Add 'doesExist'
|
|
||||||
* Exception handling of `doesExist`, `doesFileExist`, `doesDirectoryExist` has changed: only eNOENT is catched
|
|
||||||
* Exception handling of `isWritable` has changed: just a wrapper around `access` now
|
|
||||||
* switch exception handling to `safe-exceptions`
|
|
||||||
* Redo file reading API (readFileEOF dropped and now using streamly under the hood, added `readFileStream`)
|
|
||||||
|
|
||||||
|
|
||||||
## 0.10.0 -- 2020-01-04
|
|
||||||
|
|
||||||
* First version. Split from 'hpath', contains only the IO parts.
|
|
||||||
* Now uses streamly for 'copyFile'
|
|
||||||
* Fixed tmpdir in hspec
|
|
@ -1,30 +0,0 @@
|
|||||||
Copyright (c) 2020, Julian Ospald
|
|
||||||
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following
|
|
||||||
disclaimer in the documentation and/or other materials provided
|
|
||||||
with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of Julian Ospald nor the names of other
|
|
||||||
contributors may be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,27 +0,0 @@
|
|||||||
# HPath-IO
|
|
||||||
|
|
||||||
[![Gitter chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/hasufell/hpath?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Hackage version](https://img.shields.io/hackage/v/hpath-io.svg?label=Hackage)](https://hackage.haskell.org/package/hpath-io) [![Build Status](https://api.travis-ci.org/hasufell/hpath.png?branch=master)](http://travis-ci.org/hasufell/hpath) [![Hackage-Deps](https://img.shields.io/hackage-deps/v/hpath-io.svg)](http://packdeps.haskellers.com/feed?needle=hpath-io)
|
|
||||||
|
|
||||||
High-level IO operations on files/directories, utilizing type-safe Paths. This uses [hpath-directory](https://hackage.haskell.org/package/hpath-directory) under the hood.
|
|
||||||
|
|
||||||
This package is part of the HPath suite, also check out:
|
|
||||||
|
|
||||||
* [hpath](https://hackage.haskell.org/package/hpath)
|
|
||||||
* [hpath-directory](https://hackage.haskell.org/package/hpath-directory)
|
|
||||||
* [hpath-filepath](https://hackage.haskell.org/package/hpath-filepath)
|
|
||||||
|
|
||||||
## Motivation
|
|
||||||
|
|
||||||
The motivation came during development of
|
|
||||||
[hsfm](https://github.com/hasufell/hsfm)
|
|
||||||
in order to have a proper high-level API of file related operations,
|
|
||||||
while utilizing type-safe Paths.
|
|
||||||
|
|
||||||
## Goals
|
|
||||||
|
|
||||||
* high-level API to file operations like recursive directory copy
|
|
||||||
* still allowing sufficient control to interact with the underlying low-level calls
|
|
||||||
* unit-testing exceptions (because yes, people may rely on them)
|
|
||||||
|
|
||||||
Note: this library was written for __posix__ systems and it will probably not support other systems.
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
|||||||
import Distribution.Simple
|
|
||||||
main = defaultMain
|
|
@ -1,6 +0,0 @@
|
|||||||
# TODO
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
* `doesExist` not tested
|
|
||||||
* `readFileStream` only implicitly tested by `readFile`
|
|
@ -1,46 +0,0 @@
|
|||||||
name: hpath-io
|
|
||||||
version: 0.13.2
|
|
||||||
synopsis: High-level IO operations on files/directories
|
|
||||||
description: High-level IO operations on files/directories, utilizing type-safe Paths
|
|
||||||
-- bug-reports:
|
|
||||||
license: BSD3
|
|
||||||
license-file: LICENSE
|
|
||||||
author: Julian Ospald <hasufell@posteo.de>
|
|
||||||
maintainer: Julian Ospald <hasufell@posteo.de>
|
|
||||||
copyright: Julian Ospald 2016
|
|
||||||
category: Filesystem
|
|
||||||
build-type: Simple
|
|
||||||
cabal-version: 1.14
|
|
||||||
tested-with: GHC==7.10.3
|
|
||||||
, GHC==8.0.2
|
|
||||||
, GHC==8.2.2
|
|
||||||
, GHC==8.4.4
|
|
||||||
, GHC==8.6.5
|
|
||||||
, GHC==8.8.1
|
|
||||||
extra-source-files: README.md
|
|
||||||
CHANGELOG.md
|
|
||||||
|
|
||||||
library
|
|
||||||
if os(windows)
|
|
||||||
build-depends: unbuildable<0
|
|
||||||
buildable: False
|
|
||||||
exposed-modules: HPath.IO
|
|
||||||
build-depends: base >= 4.8 && <5
|
|
||||||
, bytestring >= 0.10.0.0
|
|
||||||
, exceptions
|
|
||||||
, hpath >= 0.11 && < 0.12
|
|
||||||
, hpath-directory >= 0.13 && < 0.14
|
|
||||||
, hpath-posix >= 0.13 && < 0.14
|
|
||||||
, safe-exceptions >= 0.1
|
|
||||||
, streamly >= 0.7
|
|
||||||
, time >= 1.8
|
|
||||||
, unix >= 2.5
|
|
||||||
if !impl(ghc>=7.11)
|
|
||||||
build-depends: transformers
|
|
||||||
hs-source-dirs: src
|
|
||||||
default-language: Haskell2010
|
|
||||||
|
|
||||||
|
|
||||||
source-repository head
|
|
||||||
type: git
|
|
||||||
location: https://github.com/hasufell/hpath
|
|
@ -1,865 +0,0 @@
|
|||||||
-- |
|
|
||||||
-- Module : HPath.IO
|
|
||||||
-- Copyright : © 2016 Julian Ospald
|
|
||||||
-- License : BSD3
|
|
||||||
--
|
|
||||||
-- Maintainer : Julian Ospald <hasufell@posteo.de>
|
|
||||||
-- Stability : experimental
|
|
||||||
-- Portability : portable
|
|
||||||
--
|
|
||||||
-- This module provides high-level IO related file operations like
|
|
||||||
-- copy, delete, move and so on. It only operates on /Path x/ which
|
|
||||||
-- guarantees us well-typed paths. This is a thin wrapper over
|
|
||||||
-- System.Posix.RawFilePath.Directory in 'hpath-directory'. It's
|
|
||||||
-- encouraged to use this module.
|
|
||||||
--
|
|
||||||
-- Some of these operations are due to their nature __not atomic__, which
|
|
||||||
-- means they may do multiple syscalls which form one context. Some
|
|
||||||
-- of them also have to examine the filetypes explicitly before the
|
|
||||||
-- syscalls, so a reasonable decision can be made. That means
|
|
||||||
-- the result is undefined if another process changes that context
|
|
||||||
-- while the non-atomic operation is still happening. However, where
|
|
||||||
-- possible, as few syscalls as possible are used and the underlying
|
|
||||||
-- exception handling is kept.
|
|
||||||
--
|
|
||||||
-- Note: `BlockDevice`, `CharacterDevice`, `NamedPipe` and `Socket`
|
|
||||||
-- are ignored by some of the more high-level functions (like `easyCopy`).
|
|
||||||
-- For other functions (like `copyFile`), the behavior on these file types is
|
|
||||||
-- unreliable/unsafe. Check the documentation of those functions for details.
|
|
||||||
|
|
||||||
{-# LANGUAGE FlexibleContexts #-} -- streamly
|
|
||||||
{-# LANGUAGE PackageImports #-}
|
|
||||||
|
|
||||||
module HPath.IO
|
|
||||||
(
|
|
||||||
-- * Types
|
|
||||||
FileType(..)
|
|
||||||
, RecursiveErrorMode(..)
|
|
||||||
, CopyMode(..)
|
|
||||||
-- * File copying
|
|
||||||
, copyDirRecursive
|
|
||||||
, recreateSymlink
|
|
||||||
, copyFile
|
|
||||||
, easyCopy
|
|
||||||
-- * File deletion
|
|
||||||
, deleteFile
|
|
||||||
, deleteDir
|
|
||||||
, deleteDirRecursive
|
|
||||||
, easyDelete
|
|
||||||
-- * File opening
|
|
||||||
, openFile
|
|
||||||
, executeFile
|
|
||||||
-- * File creation
|
|
||||||
, createRegularFile
|
|
||||||
, createDir
|
|
||||||
, createDirIfMissing
|
|
||||||
, createDirRecursive
|
|
||||||
, createSymlink
|
|
||||||
-- * File renaming/moving
|
|
||||||
, renameFile
|
|
||||||
, moveFile
|
|
||||||
-- * File reading
|
|
||||||
, readFile
|
|
||||||
, readFileStream
|
|
||||||
-- * File writing
|
|
||||||
, writeFile
|
|
||||||
, writeFileL
|
|
||||||
, appendFile
|
|
||||||
-- * File permissions
|
|
||||||
, RD.newFilePerms
|
|
||||||
, RD.newDirPerms
|
|
||||||
-- * File checks
|
|
||||||
, doesExist
|
|
||||||
, doesFileExist
|
|
||||||
, doesDirectoryExist
|
|
||||||
, isReadable
|
|
||||||
, isWritable
|
|
||||||
, isExecutable
|
|
||||||
, canOpenDirectory
|
|
||||||
-- * File times
|
|
||||||
, getModificationTime
|
|
||||||
, setModificationTime
|
|
||||||
, setModificationTimeHiRes
|
|
||||||
-- * Directory reading
|
|
||||||
, getDirsFiles
|
|
||||||
, getDirsFiles'
|
|
||||||
, getDirsFilesStream
|
|
||||||
-- * Filetype operations
|
|
||||||
, getFileType
|
|
||||||
-- * Others
|
|
||||||
, canonicalizePath
|
|
||||||
, toAbs
|
|
||||||
, withRawFilePath
|
|
||||||
, withHandle
|
|
||||||
, module System.Posix.RawFilePath.Directory.Errors
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
|
|
||||||
import Control.Exception.Safe ( MonadMask
|
|
||||||
, MonadCatch
|
|
||||||
, bracketOnError
|
|
||||||
, finally
|
|
||||||
)
|
|
||||||
import Control.Monad.Catch ( MonadThrow(..) )
|
|
||||||
|
|
||||||
import Data.ByteString ( ByteString )
|
|
||||||
import Data.Traversable ( for )
|
|
||||||
import qualified Data.ByteString.Lazy as L
|
|
||||||
import Data.Time.Clock
|
|
||||||
import Data.Time.Clock.POSIX ( POSIXTime )
|
|
||||||
import HPath
|
|
||||||
import Prelude hiding ( appendFile
|
|
||||||
, readFile
|
|
||||||
, writeFile
|
|
||||||
)
|
|
||||||
import Streamly
|
|
||||||
import qualified System.IO as SIO
|
|
||||||
import System.Posix.Directory.ByteString
|
|
||||||
( getWorkingDirectory )
|
|
||||||
import qualified "unix" System.Posix.IO.ByteString
|
|
||||||
as SPI
|
|
||||||
import System.Posix.FD ( openFd )
|
|
||||||
import System.Posix.RawFilePath.Directory.Errors
|
|
||||||
import System.Posix.Types ( FileMode
|
|
||||||
, ProcessID
|
|
||||||
, EpochTime
|
|
||||||
)
|
|
||||||
import qualified System.Posix.RawFilePath.Directory
|
|
||||||
as RD
|
|
||||||
import System.Posix.RawFilePath.Directory
|
|
||||||
( FileType
|
|
||||||
, RecursiveErrorMode
|
|
||||||
, CopyMode
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--------------------
|
|
||||||
--[ File Copying ]--
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- |Copies the contents of a directory recursively to the given destination, while preserving permissions.
|
|
||||||
-- Does not follow symbolic links. This behaves more or less like
|
|
||||||
-- the following, without descending into the destination if it
|
|
||||||
-- already exists:
|
|
||||||
--
|
|
||||||
-- @
|
|
||||||
-- cp -a \/source\/dir \/destination\/somedir
|
|
||||||
-- @
|
|
||||||
--
|
|
||||||
-- For directory contents, this will ignore any file type that is not
|
|
||||||
-- `RegularFile`, `SymbolicLink` or `Directory`.
|
|
||||||
--
|
|
||||||
-- For `Overwrite` copy mode this does not prune destination directory
|
|
||||||
-- contents, so the destination might contain more files than the source after
|
|
||||||
-- the operation has completed. Permissions of existing directories are
|
|
||||||
-- fixed.
|
|
||||||
--
|
|
||||||
-- Safety/reliability concerns:
|
|
||||||
--
|
|
||||||
-- * not atomic
|
|
||||||
-- * examines filetypes explicitly
|
|
||||||
-- * an explicit check `throwDestinationInSource` is carried out for the
|
|
||||||
-- top directory for basic sanity, because otherwise we might end up
|
|
||||||
-- with an infinite copy loop... however, this operation is not
|
|
||||||
-- carried out recursively (because it's slow)
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `NoSuchThing` if source directory does not exist
|
|
||||||
-- - `PermissionDenied` if source directory can't be opened
|
|
||||||
-- - `SameFile` if source and destination are the same file
|
|
||||||
-- (`HPathIOException`)
|
|
||||||
-- - `DestinationInSource` if destination is contained in source
|
|
||||||
-- (`HPathIOException`)
|
|
||||||
--
|
|
||||||
-- Throws in `FailEarly` RecursiveErrorMode only:
|
|
||||||
--
|
|
||||||
-- - `PermissionDenied` if output directory is not writable
|
|
||||||
-- - `InvalidArgument` if source directory is wrong type (symlink)
|
|
||||||
-- - `InappropriateType` if source directory is wrong type (regular file)
|
|
||||||
--
|
|
||||||
-- Throws in `CollectFailures` RecursiveErrorMode only:
|
|
||||||
--
|
|
||||||
-- - `RecursiveFailure` if any of the recursive operations that are not
|
|
||||||
-- part of the top-directory sanity-checks fail (`HPathIOException`)
|
|
||||||
--
|
|
||||||
-- Throws in `Strict` CopyMode only:
|
|
||||||
--
|
|
||||||
-- - `AlreadyExists` if destination already exists
|
|
||||||
--
|
|
||||||
-- Note: may call `getcwd` (only if destination is a relative path)
|
|
||||||
copyDirRecursive :: Path b1 -- ^ source dir
|
|
||||||
-> Path b2 -- ^ destination (parent dirs
|
|
||||||
-- are not automatically created)
|
|
||||||
-> CopyMode
|
|
||||||
-> RecursiveErrorMode
|
|
||||||
-> IO ()
|
|
||||||
copyDirRecursive (Path fromp) (Path destdirp) cm rm =
|
|
||||||
RD.copyDirRecursive fromp destdirp cm rm
|
|
||||||
|
|
||||||
|
|
||||||
-- |Recreate a symlink.
|
|
||||||
--
|
|
||||||
-- In `Overwrite` copy mode only files and empty directories are deleted.
|
|
||||||
--
|
|
||||||
-- Safety/reliability concerns:
|
|
||||||
--
|
|
||||||
-- * `Overwrite` mode is inherently non-atomic
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `InvalidArgument` if source file is wrong type (not a symlink)
|
|
||||||
-- - `PermissionDenied` if output directory cannot be written to
|
|
||||||
-- - `PermissionDenied` if source directory cannot be opened
|
|
||||||
-- - `SameFile` if source and destination are the same file
|
|
||||||
-- (`HPathIOException`)
|
|
||||||
--
|
|
||||||
--
|
|
||||||
-- Throws in `Strict` mode only:
|
|
||||||
--
|
|
||||||
-- - `AlreadyExists` if destination already exists
|
|
||||||
--
|
|
||||||
-- Throws in `Overwrite` mode only:
|
|
||||||
--
|
|
||||||
-- - `UnsatisfiedConstraints` if destination file is non-empty directory
|
|
||||||
--
|
|
||||||
-- Notes:
|
|
||||||
--
|
|
||||||
-- - calls `symlink`
|
|
||||||
-- - calls `getcwd` in Overwrite mode (if destination is a relative path)
|
|
||||||
recreateSymlink :: Path b1 -- ^ the old symlink file
|
|
||||||
-> Path b2 -- ^ destination file
|
|
||||||
-> CopyMode
|
|
||||||
-> IO ()
|
|
||||||
recreateSymlink (Path symsourceBS) (Path newsymBS) cm =
|
|
||||||
RD.recreateSymlink symsourceBS newsymBS cm
|
|
||||||
|
|
||||||
|
|
||||||
-- |Copies the given regular file to the given destination.
|
|
||||||
-- Neither follows symbolic links, nor accepts them.
|
|
||||||
-- For "copying" symbolic links, use `recreateSymlink` instead.
|
|
||||||
--
|
|
||||||
-- Note that this is still sort of a low-level function and doesn't
|
|
||||||
-- examine file types. For a more high-level version, use `easyCopy`
|
|
||||||
-- instead.
|
|
||||||
--
|
|
||||||
-- In `Overwrite` copy mode only overwrites actual files, not directories.
|
|
||||||
-- In `Strict` mode the destination file must not exist.
|
|
||||||
--
|
|
||||||
-- Safety/reliability concerns:
|
|
||||||
--
|
|
||||||
-- * `Overwrite` mode is not atomic
|
|
||||||
-- * when used on `CharacterDevice`, reads the "contents" and copies
|
|
||||||
-- them to a regular file, which might take indefinitely
|
|
||||||
-- * when used on `BlockDevice`, may either read the "contents"
|
|
||||||
-- and copy them to a regular file (potentially hanging indefinitely)
|
|
||||||
-- or may create a regular empty destination file
|
|
||||||
-- * when used on `NamedPipe`, will hang indefinitely
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `NoSuchThing` if source file does not exist
|
|
||||||
-- - `NoSuchThing` if source file is a a `Socket`
|
|
||||||
-- - `PermissionDenied` if output directory is not writable
|
|
||||||
-- - `PermissionDenied` if source directory can't be opened
|
|
||||||
-- - `InvalidArgument` if source file is wrong type (symlink or directory)
|
|
||||||
-- - `SameFile` if source and destination are the same file
|
|
||||||
-- (`HPathIOException`)
|
|
||||||
--
|
|
||||||
-- Throws in `Strict` mode only:
|
|
||||||
--
|
|
||||||
-- - `AlreadyExists` if destination already exists
|
|
||||||
--
|
|
||||||
-- Notes:
|
|
||||||
--
|
|
||||||
-- - may call `getcwd` in Overwrite mode (if destination is a relative path)
|
|
||||||
copyFile :: Path b1 -- ^ source file
|
|
||||||
-> Path b2 -- ^ destination file
|
|
||||||
-> CopyMode
|
|
||||||
-> IO ()
|
|
||||||
copyFile (Path from) (Path to) cm = RD.copyFile from to cm
|
|
||||||
|
|
||||||
-- |Copies a regular file, directory or symbolic link. In case of a
|
|
||||||
-- symbolic link it is just recreated, even if it points to a directory.
|
|
||||||
-- Any other file type is ignored.
|
|
||||||
--
|
|
||||||
-- Safety/reliability concerns:
|
|
||||||
--
|
|
||||||
-- * examines filetypes explicitly
|
|
||||||
-- * calls `copyDirRecursive` for directories
|
|
||||||
--
|
|
||||||
-- Note: may call `getcwd` in Overwrite mode (if destination is a relative path)
|
|
||||||
easyCopy :: Path b1 -> Path b2 -> CopyMode -> RecursiveErrorMode -> IO ()
|
|
||||||
easyCopy (Path from) (Path to) cm rm = RD.easyCopy from to cm rm
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---------------------
|
|
||||||
--[ File Deletion ]--
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
|
|
||||||
-- |Deletes the given file. Raises `eISDIR`
|
|
||||||
-- if run on a directory. Does not follow symbolic links.
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `InappropriateType` for wrong file type (directory)
|
|
||||||
-- - `NoSuchThing` if the file does not exist
|
|
||||||
-- - `PermissionDenied` if the directory cannot be read
|
|
||||||
deleteFile :: Path b -> IO ()
|
|
||||||
deleteFile (Path p) = RD.deleteFile p
|
|
||||||
|
|
||||||
|
|
||||||
-- |Deletes the given directory, which must be empty, never symlinks.
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `InappropriateType` for wrong file type (symlink to directory)
|
|
||||||
-- - `InappropriateType` for wrong file type (regular file)
|
|
||||||
-- - `NoSuchThing` if directory does not exist
|
|
||||||
-- - `UnsatisfiedConstraints` if directory is not empty
|
|
||||||
-- - `PermissionDenied` if we can't open or write to parent directory
|
|
||||||
--
|
|
||||||
-- Notes: calls `rmdir`
|
|
||||||
deleteDir :: Path b -> IO ()
|
|
||||||
deleteDir (Path p) = RD.deleteDir p
|
|
||||||
|
|
||||||
|
|
||||||
-- |Deletes the given directory recursively. Does not follow symbolic
|
|
||||||
-- links. Tries `deleteDir` first before attemtping a recursive
|
|
||||||
-- deletion.
|
|
||||||
--
|
|
||||||
-- On directory contents this behaves like `easyDelete`
|
|
||||||
-- and thus will ignore any file type that is not `RegularFile`,
|
|
||||||
-- `SymbolicLink` or `Directory`.
|
|
||||||
--
|
|
||||||
-- Safety/reliability concerns:
|
|
||||||
--
|
|
||||||
-- * not atomic
|
|
||||||
-- * examines filetypes explicitly
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `InappropriateType` for wrong file type (symlink to directory)
|
|
||||||
-- - `InappropriateType` for wrong file type (regular file)
|
|
||||||
-- - `NoSuchThing` if directory does not exist
|
|
||||||
-- - `PermissionDenied` if we can't open or write to parent directory
|
|
||||||
deleteDirRecursive :: Path b -> IO ()
|
|
||||||
deleteDirRecursive (Path p) = RD.deleteDirRecursive p
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- |Deletes a file, directory or symlink.
|
|
||||||
-- In case of directory, performs recursive deletion. In case of
|
|
||||||
-- a symlink, the symlink file is deleted.
|
|
||||||
-- Any other file type is ignored.
|
|
||||||
--
|
|
||||||
-- Safety/reliability concerns:
|
|
||||||
--
|
|
||||||
-- * examines filetypes explicitly
|
|
||||||
-- * calls `deleteDirRecursive` for directories
|
|
||||||
easyDelete :: Path b -> IO ()
|
|
||||||
easyDelete (Path p) = RD.easyDelete p
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--------------------
|
|
||||||
--[ File Opening ]--
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
|
|
||||||
-- |Opens a file appropriately by invoking xdg-open. The file type
|
|
||||||
-- is not checked. This forks a process.
|
|
||||||
openFile :: Path b -> IO ProcessID
|
|
||||||
openFile (Path fp) = RD.openFile fp
|
|
||||||
|
|
||||||
|
|
||||||
-- |Executes a program with the given arguments. This forks a process.
|
|
||||||
executeFile :: Path b -- ^ program
|
|
||||||
-> [ByteString] -- ^ arguments
|
|
||||||
-> IO ProcessID
|
|
||||||
executeFile (Path fp) args = RD.executeFile fp args
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---------------------
|
|
||||||
--[ File Creation ]--
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
|
|
||||||
-- |Create an empty regular file at the given directory with the given
|
|
||||||
-- filename.
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `PermissionDenied` if output directory cannot be written to
|
|
||||||
-- - `AlreadyExists` if destination already exists
|
|
||||||
-- - `NoSuchThing` if any of the parent components of the path
|
|
||||||
-- do not exist
|
|
||||||
createRegularFile :: FileMode -> Path b -> IO ()
|
|
||||||
createRegularFile fm (Path destBS) = RD.createRegularFile fm destBS
|
|
||||||
|
|
||||||
|
|
||||||
-- |Create an empty directory at the given directory with the given filename.
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `PermissionDenied` if output directory cannot be written to
|
|
||||||
-- - `AlreadyExists` if destination already exists
|
|
||||||
-- - `NoSuchThing` if any of the parent components of the path
|
|
||||||
-- do not exist
|
|
||||||
createDir :: FileMode -> Path b -> IO ()
|
|
||||||
createDir fm (Path destBS) = RD.createDir fm destBS
|
|
||||||
|
|
||||||
-- |Create an empty directory at the given directory with the given filename.
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `PermissionDenied` if output directory cannot be written to
|
|
||||||
-- - `NoSuchThing` if any of the parent components of the path
|
|
||||||
-- do not exist
|
|
||||||
createDirIfMissing :: FileMode -> Path b -> IO ()
|
|
||||||
createDirIfMissing fm (Path destBS) = RD.createDirIfMissing fm destBS
|
|
||||||
|
|
||||||
|
|
||||||
-- |Create an empty directory at the given directory with the given filename.
|
|
||||||
-- All parent directories are created with the same filemode. This
|
|
||||||
-- basically behaves like:
|
|
||||||
--
|
|
||||||
-- @
|
|
||||||
-- mkdir -p \/some\/dir
|
|
||||||
-- @
|
|
||||||
--
|
|
||||||
-- Safety/reliability concerns:
|
|
||||||
--
|
|
||||||
-- * not atomic
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `PermissionDenied` if any part of the path components do not
|
|
||||||
-- exist and cannot be written to
|
|
||||||
-- - `AlreadyExists` if destination already exists and
|
|
||||||
-- is *not* a directory
|
|
||||||
--
|
|
||||||
-- Note: calls `getcwd` if the input path is a relative path
|
|
||||||
createDirRecursive :: FileMode -> Path b -> IO ()
|
|
||||||
createDirRecursive fm (Path p) = RD.createDirRecursive fm p
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- |Create a symlink.
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `PermissionDenied` if output directory cannot be written to
|
|
||||||
-- - `AlreadyExists` if destination file already exists
|
|
||||||
-- - `NoSuchThing` if any of the parent components of the path
|
|
||||||
-- do not exist
|
|
||||||
--
|
|
||||||
-- Note: calls `symlink`
|
|
||||||
createSymlink :: Path b -- ^ destination file
|
|
||||||
-> ByteString -- ^ path the symlink points to
|
|
||||||
-> IO ()
|
|
||||||
createSymlink (Path destBS) sympoint = RD.createSymlink destBS sympoint
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----------------------------
|
|
||||||
--[ File Renaming/Moving ]--
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
|
|
||||||
-- |Rename a given file with the provided filename. Destination and source
|
|
||||||
-- must be on the same device, otherwise `eXDEV` will be raised.
|
|
||||||
--
|
|
||||||
-- Does not follow symbolic links, but renames the symbolic link file.
|
|
||||||
--
|
|
||||||
-- Safety/reliability concerns:
|
|
||||||
--
|
|
||||||
-- * has a separate set of exception handling, apart from the syscall
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `NoSuchThing` if source file does not exist
|
|
||||||
-- - `PermissionDenied` if output directory cannot be written to
|
|
||||||
-- - `PermissionDenied` if source directory cannot be opened
|
|
||||||
-- - `UnsupportedOperation` if source and destination are on different
|
|
||||||
-- devices
|
|
||||||
-- - `AlreadyExists` if destination already exists
|
|
||||||
-- - `SameFile` if destination and source are the same file
|
|
||||||
-- (`HPathIOException`)
|
|
||||||
--
|
|
||||||
-- Note: calls `rename` (but does not allow to rename over existing files)
|
|
||||||
renameFile :: Path b1 -> Path b2 -> IO ()
|
|
||||||
renameFile (Path from) (Path to) = RD.renameFile from to
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- |Move a file. This also works across devices by copy-delete fallback.
|
|
||||||
-- And also works on directories.
|
|
||||||
--
|
|
||||||
-- Does not follow symbolic links, but renames the symbolic link file.
|
|
||||||
--
|
|
||||||
--
|
|
||||||
-- Safety/reliability concerns:
|
|
||||||
--
|
|
||||||
-- * `Overwrite` mode is not atomic
|
|
||||||
-- * copy-delete fallback is inherently non-atomic
|
|
||||||
-- * since this function calls `easyCopy` and `easyDelete` as a fallback
|
|
||||||
-- to `renameFile`, file types that are not `RegularFile`, `SymbolicLink`
|
|
||||||
-- or `Directory` may be ignored
|
|
||||||
-- * for `Overwrite` mode, the destination will be deleted (not recursively)
|
|
||||||
-- before moving
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `NoSuchThing` if source file does not exist
|
|
||||||
-- - `PermissionDenied` if output directory cannot be written to
|
|
||||||
-- - `PermissionDenied` if source directory cannot be opened
|
|
||||||
-- - `SameFile` if destination and source are the same file
|
|
||||||
-- (`HPathIOException`)
|
|
||||||
--
|
|
||||||
-- Throws in `Strict` mode only:
|
|
||||||
--
|
|
||||||
-- - `AlreadyExists` if destination already exists
|
|
||||||
--
|
|
||||||
-- Notes:
|
|
||||||
--
|
|
||||||
-- - calls `rename` (but does not allow to rename over existing files)
|
|
||||||
-- - calls `getcwd` in Overwrite mode if destination is a relative path
|
|
||||||
moveFile :: Path b1 -- ^ file to move
|
|
||||||
-> Path b2 -- ^ destination
|
|
||||||
-> CopyMode
|
|
||||||
-> IO ()
|
|
||||||
moveFile (Path from) (Path to) cm = RD.moveFile from to cm
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--------------------
|
|
||||||
--[ File Reading ]--
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
|
|
||||||
-- |Read the given file *at once* into memory as a lazy ByteString.
|
|
||||||
-- Symbolic links are followed, no sanity checks on file size
|
|
||||||
-- or file type. File must exist. Uses Builders under the hood
|
|
||||||
-- (hence lazy ByteString).
|
|
||||||
--
|
|
||||||
-- Safety/reliability concerns:
|
|
||||||
--
|
|
||||||
-- * the whole file is read into memory, this doesn't read lazily
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `InappropriateType` if file is not a regular file or a symlink
|
|
||||||
-- - `PermissionDenied` if we cannot read the file or the directory
|
|
||||||
-- containting it
|
|
||||||
-- - `NoSuchThing` if the file does not exist
|
|
||||||
readFile :: Path b -> IO L.ByteString
|
|
||||||
readFile (Path path) = RD.readFile path
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- | Open the given file as a filestream. Once the filestream is
|
|
||||||
-- exits, the filehandle is cleaned up.
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `InappropriateType` if file is not a regular file or a symlink
|
|
||||||
-- - `PermissionDenied` if we cannot read the file or the directory
|
|
||||||
-- containting it
|
|
||||||
-- - `NoSuchThing` if the file does not exist
|
|
||||||
readFileStream :: Path b -> IO (SerialT IO ByteString)
|
|
||||||
readFileStream (Path fp) = RD.readFileStream fp
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--------------------
|
|
||||||
--[ File Writing ]--
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
|
|
||||||
-- |Write a given ByteString to a file, truncating the file beforehand.
|
|
||||||
-- Follows symlinks.
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `InappropriateType` if file is not a regular file or a symlink
|
|
||||||
-- - `PermissionDenied` if we cannot read the file or the directory
|
|
||||||
-- containting it
|
|
||||||
-- - `NoSuchThing` if the file does not exist
|
|
||||||
writeFile :: Path b
|
|
||||||
-> Maybe FileMode -- ^ if Nothing, file must exist
|
|
||||||
-> ByteString
|
|
||||||
-> IO ()
|
|
||||||
writeFile (Path fp) fmode bs = RD.writeFile fp fmode bs
|
|
||||||
|
|
||||||
|
|
||||||
-- |Write a given lazy ByteString to a file, truncating the file beforehand.
|
|
||||||
-- Follows symlinks.
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `InappropriateType` if file is not a regular file or a symlink
|
|
||||||
-- - `PermissionDenied` if we cannot read the file or the directory
|
|
||||||
-- containting it
|
|
||||||
-- - `NoSuchThing` if the file does not exist
|
|
||||||
--
|
|
||||||
-- Note: uses streamly under the hood
|
|
||||||
writeFileL :: Path b
|
|
||||||
-> Maybe FileMode -- ^ if Nothing, file must exist
|
|
||||||
-> L.ByteString
|
|
||||||
-> IO ()
|
|
||||||
writeFileL (Path fp) fmode lbs = RD.writeFileL fp fmode lbs
|
|
||||||
|
|
||||||
|
|
||||||
-- |Append a given ByteString to a file.
|
|
||||||
-- The file must exist. Follows symlinks.
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `InappropriateType` if file is not a regular file or a symlink
|
|
||||||
-- - `PermissionDenied` if we cannot read the file or the directory
|
|
||||||
-- containting it
|
|
||||||
-- - `NoSuchThing` if the file does not exist
|
|
||||||
appendFile :: Path b -> ByteString -> IO ()
|
|
||||||
appendFile (Path fp) bs = RD.appendFile fp bs
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-------------------
|
|
||||||
--[ File checks ]--
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
|
|
||||||
-- |Checks if the given file exists.
|
|
||||||
-- Does not follow symlinks.
|
|
||||||
--
|
|
||||||
-- Only eNOENT is catched (and returns False).
|
|
||||||
doesExist :: Path b -> IO Bool
|
|
||||||
doesExist (Path bs) = RD.doesExist bs
|
|
||||||
|
|
||||||
|
|
||||||
-- |Checks if the given file exists and is not a directory.
|
|
||||||
-- Does not follow symlinks.
|
|
||||||
--
|
|
||||||
-- Only eNOENT is catched (and returns False).
|
|
||||||
doesFileExist :: Path b -> IO Bool
|
|
||||||
doesFileExist (Path bs) = RD.doesFileExist bs
|
|
||||||
|
|
||||||
|
|
||||||
-- |Checks if the given file exists and is a directory.
|
|
||||||
-- Does not follow symlinks.
|
|
||||||
--
|
|
||||||
-- Only eNOENT is catched (and returns False).
|
|
||||||
doesDirectoryExist :: Path b -> IO Bool
|
|
||||||
doesDirectoryExist (Path bs) = RD.doesDirectoryExist bs
|
|
||||||
|
|
||||||
|
|
||||||
-- |Checks whether a file or folder is readable.
|
|
||||||
--
|
|
||||||
-- Only eACCES, eROFS, eTXTBSY, ePERM are catched (and return False).
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `NoSuchThing` if the file does not exist
|
|
||||||
isReadable :: Path b -> IO Bool
|
|
||||||
isReadable (Path bs) = RD.isReadable bs
|
|
||||||
|
|
||||||
-- |Checks whether a file or folder is writable.
|
|
||||||
--
|
|
||||||
-- Only eACCES, eROFS, eTXTBSY, ePERM are catched (and return False).
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `NoSuchThing` if the file does not exist
|
|
||||||
isWritable :: Path b -> IO Bool
|
|
||||||
isWritable (Path bs) = RD.isWritable bs
|
|
||||||
|
|
||||||
|
|
||||||
-- |Checks whether a file or folder is executable.
|
|
||||||
--
|
|
||||||
-- Only eACCES, eROFS, eTXTBSY, ePERM are catched (and return False).
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `NoSuchThing` if the file does not exist
|
|
||||||
isExecutable :: Path b -> IO Bool
|
|
||||||
isExecutable (Path bs) = RD.isExecutable bs
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- |Checks whether the directory at the given path exists and can be
|
|
||||||
-- opened. This invokes `openDirStream` which follows symlinks.
|
|
||||||
canOpenDirectory :: Path b -> IO Bool
|
|
||||||
canOpenDirectory (Path bs) = RD.canOpenDirectory bs
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
------------------
|
|
||||||
--[ File times ]--
|
|
||||||
------------------
|
|
||||||
|
|
||||||
|
|
||||||
getModificationTime :: Path b -> IO UTCTime
|
|
||||||
getModificationTime (Path bs) = RD.getModificationTime bs
|
|
||||||
|
|
||||||
setModificationTime :: Path b -> EpochTime -> IO ()
|
|
||||||
setModificationTime (Path bs) t = RD.setModificationTime bs t
|
|
||||||
|
|
||||||
setModificationTimeHiRes :: Path b -> POSIXTime -> IO ()
|
|
||||||
setModificationTimeHiRes (Path bs) t = RD.setModificationTimeHiRes bs t
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-------------------------
|
|
||||||
--[ Directory reading ]--
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
|
|
||||||
-- |Gets all filenames of the given directory. This excludes "." and "..".
|
|
||||||
-- This version does not follow symbolic links.
|
|
||||||
--
|
|
||||||
-- The contents are not sorted and there is no guarantee on the ordering.
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `NoSuchThing` if directory does not exist
|
|
||||||
-- - `InappropriateType` if file type is wrong (file)
|
|
||||||
-- - `InappropriateType` if file type is wrong (symlink to file)
|
|
||||||
-- - `InappropriateType` if file type is wrong (symlink to dir)
|
|
||||||
-- - `PermissionDenied` if directory cannot be opened
|
|
||||||
-- - `PathParseException` if a filename could not be parsed (should never happen)
|
|
||||||
getDirsFiles :: Path b -- ^ dir to read
|
|
||||||
-> IO [Path b]
|
|
||||||
getDirsFiles p = do
|
|
||||||
contents <- getDirsFiles' p
|
|
||||||
pure $ fmap (p </>) contents
|
|
||||||
|
|
||||||
|
|
||||||
-- | Like 'getDirsFiles', but returns the filename only, instead
|
|
||||||
-- of prepending the base path.
|
|
||||||
getDirsFiles' :: Path b -- ^ dir to read
|
|
||||||
-> IO [Path Rel]
|
|
||||||
getDirsFiles' (Path fp) = do
|
|
||||||
rawContents <- RD.getDirsFiles' fp
|
|
||||||
for rawContents $ \r -> parseRel r
|
|
||||||
|
|
||||||
|
|
||||||
-- | Like 'getDirsFiles'', except returning a Stream.
|
|
||||||
getDirsFilesStream :: (MonadCatch m, MonadAsync m, MonadMask m)
|
|
||||||
=> Path b
|
|
||||||
-> IO (SerialT m (Path Rel))
|
|
||||||
getDirsFilesStream (Path fp) = do
|
|
||||||
s <- RD.getDirsFilesStream fp
|
|
||||||
pure (s >>= parseRel)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---------------------------
|
|
||||||
--[ FileType operations ]--
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
|
|
||||||
-- |Get the file type of the file located at the given path. Does
|
|
||||||
-- not follow symbolic links.
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `NoSuchThing` if the file does not exist
|
|
||||||
-- - `PermissionDenied` if any part of the path is not accessible
|
|
||||||
getFileType :: Path b -> IO FileType
|
|
||||||
getFileType (Path fp) = RD.getFileType fp
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--------------
|
|
||||||
--[ Others ]--
|
|
||||||
--------------
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- |Applies `realpath` on the given path.
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `NoSuchThing` if the file at the given path does not exist
|
|
||||||
-- - `NoSuchThing` if the symlink is broken
|
|
||||||
-- - `PathParseException` if realpath does not return an absolute path
|
|
||||||
canonicalizePath :: Path b -> IO (Path Abs)
|
|
||||||
canonicalizePath (Path l) = do
|
|
||||||
nl <- RD.canonicalizePath l
|
|
||||||
parseAbs nl
|
|
||||||
|
|
||||||
|
|
||||||
-- |Converts any path to an absolute path.
|
|
||||||
-- This is done in the following way:
|
|
||||||
--
|
|
||||||
-- - if the path is already an absolute one, just return it
|
|
||||||
-- - if it's a relative path, prepend the current directory to it
|
|
||||||
toAbs :: Path b -> IO (Path Abs)
|
|
||||||
toAbs (Path bs) = do
|
|
||||||
let mabs = parseAbs bs :: Maybe (Path Abs)
|
|
||||||
case mabs of
|
|
||||||
Just a -> return a
|
|
||||||
Nothing -> do
|
|
||||||
cwd <- getWorkingDirectory >>= parseAbs
|
|
||||||
r <- parseRel bs -- we know it must be relative now
|
|
||||||
return $ cwd </> r
|
|
||||||
|
|
||||||
|
|
||||||
-- | Helper function to use the Path library without
|
|
||||||
-- buying into the Path type too much. This uses 'parseAny'
|
|
||||||
-- under the hood and may throw `PathParseException`.
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `PathParseException` if the bytestring could neither be parsed as
|
|
||||||
-- relative or absolute Path
|
|
||||||
withRawFilePath :: MonadThrow m
|
|
||||||
=> ByteString
|
|
||||||
-> (Either (Path Abs) (Path Rel) -> m b)
|
|
||||||
-> m b
|
|
||||||
withRawFilePath bs action = do
|
|
||||||
path <- parseAny bs
|
|
||||||
action path
|
|
||||||
|
|
||||||
|
|
||||||
-- | Convenience function to open the path as a handle.
|
|
||||||
--
|
|
||||||
-- If the file does not exist, it will be created with 'newFilePerms'.
|
|
||||||
--
|
|
||||||
-- Throws:
|
|
||||||
--
|
|
||||||
-- - `PathParseException` if the bytestring could neither be parsed as
|
|
||||||
-- relative or absolute Path
|
|
||||||
withHandle :: ByteString
|
|
||||||
-> SPI.OpenMode
|
|
||||||
-> ((SIO.Handle, Either (Path Abs) (Path Rel)) -> IO a)
|
|
||||||
-> IO a
|
|
||||||
withHandle bs mode action = do
|
|
||||||
path <- parseAny bs
|
|
||||||
handle <-
|
|
||||||
bracketOnError (openFd bs mode [] (Just RD.newFilePerms)) (SPI.closeFd)
|
|
||||||
$ SPI.fdToHandle
|
|
||||||
finally (action (handle, path)) (SIO.hClose handle)
|
|
@ -1,14 +0,0 @@
|
|||||||
# Revision history for hpath-posix
|
|
||||||
|
|
||||||
## 0.13.2 -- 2020-04-14
|
|
||||||
|
|
||||||
* fix macOS compatibility, especially with memory bug in `fdopendir`
|
|
||||||
|
|
||||||
## 0.13.1 -- 2020-02-08
|
|
||||||
|
|
||||||
* Remove unnecessary dependencies
|
|
||||||
|
|
||||||
|
|
||||||
## 0.13.0 -- 2020-01-29
|
|
||||||
|
|
||||||
* First version. Released on an unsuspecting world.
|
|
@ -1,30 +0,0 @@
|
|||||||
Copyright (c) 2020, Julian Ospald
|
|
||||||
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following
|
|
||||||
disclaimer in the documentation and/or other materials provided
|
|
||||||
with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of Julian Ospald nor the names of other
|
|
||||||
contributors may be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,13 +0,0 @@
|
|||||||
# HPath-filepath
|
|
||||||
|
|
||||||
[![Gitter chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/hasufell/hpath?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Hackage version](https://img.shields.io/hackage/v/hpath-posix.svg?label=Hackage)](https://hackage.haskell.org/package/hpath-posix) [![Build Status](https://api.travis-ci.org/hasufell/hpath.png?branch=master)](http://travis-ci.org/hasufell/hpath) [![Hackage-Deps](https://img.shields.io/hackage-deps/v/hpath-posix.svg)](http://packdeps.haskellers.com/feed?needle=hpath-posix)
|
|
||||||
|
|
||||||
Some low-level POSIX glue code, that is not in 'unix'.
|
|
||||||
|
|
||||||
This package is part of the HPath suite, also check out:
|
|
||||||
|
|
||||||
* [hpath](https://hackage.haskell.org/package/hpath)
|
|
||||||
* [hpath-directory](https://hackage.haskell.org/package/hpath-directory)
|
|
||||||
* [hpath-filepath](https://hackage.haskell.org/package/hpath-filepath)
|
|
||||||
* [hpath-io](https://hackage.haskell.org/package/hpath-io)
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
|||||||
import Distribution.Simple
|
|
||||||
main = defaultMain
|
|
@ -1,48 +0,0 @@
|
|||||||
cabal-version: >=1.10
|
|
||||||
|
|
||||||
name: hpath-posix
|
|
||||||
version: 0.13.2
|
|
||||||
synopsis: Some low-level POSIX glue code, that is not in 'unix'
|
|
||||||
homepage: https://github.com/hasufell/hpath
|
|
||||||
bug-reports: https://github.com/hasufell/hpath/issues
|
|
||||||
license: BSD3
|
|
||||||
license-file: LICENSE
|
|
||||||
author: Julian Ospald <hasufell@posteo.de>
|
|
||||||
maintainer: Julian Ospald <hasufell@posteo.de>
|
|
||||||
copyright: Julian Ospald <hasufell@posteo.de> 2020
|
|
||||||
category: Filesystem
|
|
||||||
build-type: Simple
|
|
||||||
extra-source-files: CHANGELOG.md
|
|
||||||
cbits/dirutils.h
|
|
||||||
tested-with: GHC==7.10.3
|
|
||||||
, GHC==8.0.2
|
|
||||||
, GHC==8.2.2
|
|
||||||
, GHC==8.4.4
|
|
||||||
, GHC==8.6.5
|
|
||||||
, GHC==8.8.1
|
|
||||||
|
|
||||||
library
|
|
||||||
if os(windows)
|
|
||||||
build-depends: unbuildable<0
|
|
||||||
buildable: False
|
|
||||||
exposed-modules: System.Posix.RawFilePath.Directory.Traversals
|
|
||||||
System.Posix.Foreign
|
|
||||||
System.Posix.FD
|
|
||||||
-- other-modules:
|
|
||||||
-- other-extensions:
|
|
||||||
c-sources: cbits/dirutils.c
|
|
||||||
build-depends: base >= 4.8 && <5
|
|
||||||
, bytestring >= 0.10
|
|
||||||
, hpath-filepath >= 0.10.3
|
|
||||||
, unix >= 2.5
|
|
||||||
if impl(ghc < 8.0)
|
|
||||||
build-depends:
|
|
||||||
fail >= 4.9
|
|
||||||
|
|
||||||
hs-source-dirs: src
|
|
||||||
default-language: Haskell2010
|
|
||||||
default-extensions: PackageImports
|
|
||||||
|
|
||||||
source-repository head
|
|
||||||
type: git
|
|
||||||
location: https://github.com/hasufell/hpath
|
|
132
hpath.cabal
Normal file
132
hpath.cabal
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
name: hpath
|
||||||
|
version: 0.9.1
|
||||||
|
synopsis: Support for well-typed paths
|
||||||
|
description: Support for well-typed paths, utilizing ByteString under the hood.
|
||||||
|
license: BSD3
|
||||||
|
license-file: LICENSE
|
||||||
|
author: Julian Ospald <hasufell@posteo.de>
|
||||||
|
maintainer: Julian Ospald <hasufell@posteo.de>
|
||||||
|
copyright: Julian Ospald 2016
|
||||||
|
category: Filesystem
|
||||||
|
build-type: Simple
|
||||||
|
cabal-version: 1.14
|
||||||
|
extra-source-files: README.md
|
||||||
|
CHANGELOG
|
||||||
|
cbits/dirutils.h
|
||||||
|
doctests-hpath.hs
|
||||||
|
doctests-posix.hs
|
||||||
|
|
||||||
|
library
|
||||||
|
if os(windows)
|
||||||
|
build-depends: unbuildable<0
|
||||||
|
buildable: False
|
||||||
|
hs-source-dirs: src/
|
||||||
|
default-language: Haskell2010
|
||||||
|
if impl(ghc >= 8.0)
|
||||||
|
ghc-options: -Wall -Wno-redundant-constraints
|
||||||
|
else
|
||||||
|
ghc-options: -Wall
|
||||||
|
c-sources: cbits/dirutils.c
|
||||||
|
exposed-modules: HPath,
|
||||||
|
HPath.IO,
|
||||||
|
HPath.IO.Errors,
|
||||||
|
System.Posix.Directory.Foreign,
|
||||||
|
System.Posix.Directory.Traversals,
|
||||||
|
System.Posix.FD,
|
||||||
|
System.Posix.FilePath
|
||||||
|
other-modules: HPath.Internal
|
||||||
|
build-depends: base >= 4.2 && <5
|
||||||
|
, IfElse
|
||||||
|
, bytestring >= 0.9.2.0
|
||||||
|
, deepseq
|
||||||
|
, exceptions
|
||||||
|
, hspec
|
||||||
|
, simple-sendfile >= 0.2.24
|
||||||
|
, unix >= 2.5
|
||||||
|
, unix-bytestring
|
||||||
|
, utf8-string
|
||||||
|
, word8
|
||||||
|
|
||||||
|
|
||||||
|
test-suite doctests-hpath
|
||||||
|
if os(windows)
|
||||||
|
build-depends: unbuildable<0
|
||||||
|
buildable: False
|
||||||
|
default-language: Haskell2010
|
||||||
|
type: exitcode-stdio-1.0
|
||||||
|
ghc-options: -threaded
|
||||||
|
main-is: doctests-hpath.hs
|
||||||
|
build-depends: base
|
||||||
|
, HUnit
|
||||||
|
, QuickCheck
|
||||||
|
, doctest >= 0.8
|
||||||
|
, hpath
|
||||||
|
|
||||||
|
test-suite doctests-posix
|
||||||
|
if os(windows)
|
||||||
|
build-depends: unbuildable<0
|
||||||
|
buildable: False
|
||||||
|
default-language: Haskell2010
|
||||||
|
type: exitcode-stdio-1.0
|
||||||
|
ghc-options: -threaded
|
||||||
|
main-is: doctests-posix.hs
|
||||||
|
build-depends: base,
|
||||||
|
bytestring,
|
||||||
|
unix,
|
||||||
|
hpath,
|
||||||
|
doctest >= 0.8,
|
||||||
|
HUnit,
|
||||||
|
QuickCheck
|
||||||
|
|
||||||
|
test-suite spec
|
||||||
|
if os(windows)
|
||||||
|
build-depends: unbuildable<0
|
||||||
|
buildable: False
|
||||||
|
Type: exitcode-stdio-1.0
|
||||||
|
Default-Language: Haskell2010
|
||||||
|
Hs-Source-Dirs: test
|
||||||
|
Main-Is: Main.hs
|
||||||
|
other-modules:
|
||||||
|
HPath.IO.AppendFileSpec
|
||||||
|
HPath.IO.CanonicalizePathSpec
|
||||||
|
HPath.IO.CopyDirRecursiveCollectFailuresSpec
|
||||||
|
HPath.IO.CopyDirRecursiveOverwriteSpec
|
||||||
|
HPath.IO.CopyDirRecursiveSpec
|
||||||
|
HPath.IO.CopyFileOverwriteSpec
|
||||||
|
HPath.IO.CopyFileSpec
|
||||||
|
HPath.IO.CreateDirRecursiveSpec
|
||||||
|
HPath.IO.CreateDirSpec
|
||||||
|
HPath.IO.CreateRegularFileSpec
|
||||||
|
HPath.IO.CreateSymlinkSpec
|
||||||
|
HPath.IO.DeleteDirRecursiveSpec
|
||||||
|
HPath.IO.DeleteDirSpec
|
||||||
|
HPath.IO.DeleteFileSpec
|
||||||
|
HPath.IO.GetDirsFilesSpec
|
||||||
|
HPath.IO.GetFileTypeSpec
|
||||||
|
HPath.IO.MoveFileOverwriteSpec
|
||||||
|
HPath.IO.MoveFileSpec
|
||||||
|
HPath.IO.ReadFileEOFSpec
|
||||||
|
HPath.IO.ReadFileSpec
|
||||||
|
HPath.IO.RecreateSymlinkOverwriteSpec
|
||||||
|
HPath.IO.RecreateSymlinkSpec
|
||||||
|
HPath.IO.RenameFileSpec
|
||||||
|
HPath.IO.ToAbsSpec
|
||||||
|
HPath.IO.WriteFileSpec
|
||||||
|
Spec
|
||||||
|
Utils
|
||||||
|
GHC-Options: -Wall
|
||||||
|
Build-Depends: base
|
||||||
|
, HUnit
|
||||||
|
, IfElse
|
||||||
|
, bytestring
|
||||||
|
, hpath
|
||||||
|
, hspec >= 1.3
|
||||||
|
, process
|
||||||
|
, unix
|
||||||
|
, unix-bytestring
|
||||||
|
, utf8-string
|
||||||
|
|
||||||
|
source-repository head
|
||||||
|
type: git
|
||||||
|
location: https://github.com/hasufell/hpath
|
||||||
|
|
@ -1,40 +0,0 @@
|
|||||||
# HPath
|
|
||||||
|
|
||||||
[![Gitter chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/hasufell/hpath?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Hackage version](https://img.shields.io/hackage/v/hpath.svg?label=Hackage)](https://hackage.haskell.org/package/hpath) [![Build Status](https://api.travis-ci.org/hasufell/hpath.png?branch=master)](http://travis-ci.org/hasufell/hpath) [![Hackage-Deps](https://img.shields.io/hackage-deps/v/hpath.svg)](http://packdeps.haskellers.com/feed?needle=hpath)
|
|
||||||
|
|
||||||
Support for well-typed paths in Haskell.
|
|
||||||
|
|
||||||
This package is part of the HPath suite, also check out:
|
|
||||||
|
|
||||||
* [hpath-directory](https://hackage.haskell.org/package/hpath-directory)
|
|
||||||
* [hpath-filepath](https://hackage.haskell.org/package/hpath-filepath)
|
|
||||||
* [hpath-io](https://hackage.haskell.org/package/hpath-io)
|
|
||||||
|
|
||||||
## Motivation
|
|
||||||
|
|
||||||
The motivation came during development of
|
|
||||||
[hsfm](https://github.com/hasufell/hsfm)
|
|
||||||
which has a pretty strict File type, but lacks a strict Path type, e.g.
|
|
||||||
for user input.
|
|
||||||
|
|
||||||
The library that came closest to my needs was
|
|
||||||
[path](https://github.com/chrisdone/path),
|
|
||||||
but the API turned out to be oddly complicated for my use case, so I
|
|
||||||
decided to fork it.
|
|
||||||
|
|
||||||
## Goals
|
|
||||||
|
|
||||||
* well-typed paths
|
|
||||||
* safe filepath manipulation, never using String as filepath, but ByteString
|
|
||||||
|
|
||||||
Note: this library was written for __posix__ systems and it will probably not support other systems.
|
|
||||||
|
|
||||||
## Differences to 'path'
|
|
||||||
|
|
||||||
* doesn't attempt to fake IO-related information into the path, so whether a path points to a file or directory is up to your IO-code to decide...
|
|
||||||
* uses safe ByteString for filepaths under the hood instead of unsafe String
|
|
||||||
* fixes broken [dirname](https://github.com/chrisdone/path/issues/18)
|
|
||||||
* renames dirname/filename to basename/dirname to match the POSIX shell functions
|
|
||||||
* allows pattern matching via unidirectional PatternSynonym
|
|
||||||
* uses simple doctest for testing
|
|
||||||
* allows `~/` as relative path, because on posix level `~` is just a regular filename that does _NOT_ point to `$HOME`
|
|
@ -1,2 +0,0 @@
|
|||||||
import Distribution.Simple
|
|
||||||
main = defaultMain
|
|
@ -1,46 +0,0 @@
|
|||||||
name: hpath
|
|
||||||
version: 0.11.0
|
|
||||||
synopsis: Support for well-typed paths
|
|
||||||
description: Support for well-typed paths, utilizing ByteString under the hood.
|
|
||||||
license: BSD3
|
|
||||||
license-file: LICENSE
|
|
||||||
author: Julian Ospald <hasufell@posteo.de>
|
|
||||||
maintainer: Julian Ospald <hasufell@posteo.de>
|
|
||||||
copyright: Julian Ospald 2016
|
|
||||||
category: Filesystem
|
|
||||||
build-type: Simple
|
|
||||||
cabal-version: 1.14
|
|
||||||
tested-with: GHC==7.10.3
|
|
||||||
, GHC==8.0.2
|
|
||||||
, GHC==8.2.2
|
|
||||||
, GHC==8.4.4
|
|
||||||
, GHC==8.6.5
|
|
||||||
, GHC==8.8.1
|
|
||||||
extra-source-files: README.md
|
|
||||||
CHANGELOG
|
|
||||||
|
|
||||||
library
|
|
||||||
if os(windows)
|
|
||||||
build-depends: unbuildable<0
|
|
||||||
buildable: False
|
|
||||||
hs-source-dirs: src/
|
|
||||||
default-language: Haskell2010
|
|
||||||
if impl(ghc >= 8.0)
|
|
||||||
ghc-options: -Wall -Wno-redundant-constraints
|
|
||||||
else
|
|
||||||
ghc-options: -Wall
|
|
||||||
exposed-modules: HPath
|
|
||||||
other-modules: HPath.Internal
|
|
||||||
build-depends: base >= 4.8 && <5
|
|
||||||
, bytestring >= 0.10.0.0
|
|
||||||
, deepseq
|
|
||||||
, exceptions
|
|
||||||
, hpath-filepath >= 0.10 && < 0.11
|
|
||||||
, template-haskell
|
|
||||||
, utf8-string
|
|
||||||
, word8
|
|
||||||
|
|
||||||
source-repository head
|
|
||||||
type: git
|
|
||||||
location: https://github.com/hasufell/hpath
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
if [ -n "${SKIP_DOCTESTS}" ] ; then
|
|
||||||
echo "Skipping doctests"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v doctest >/dev/null ; then
|
|
||||||
tempdir="$(mktemp -d)"
|
|
||||||
(
|
|
||||||
cd "${tempdir}"
|
|
||||||
cabal install --installdir="${tempdir}" doctest
|
|
||||||
)
|
|
||||||
export PATH="${tempdir}:$PATH"
|
|
||||||
fi
|
|
||||||
|
|
||||||
set -x
|
|
||||||
|
|
||||||
cd "$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)"
|
|
||||||
|
|
||||||
cabal exec doctest -- -isrc -XOverloadedStrings -XQuasiQuotes HPath
|
|
@ -13,11 +13,7 @@
|
|||||||
{-# LANGUAGE CPP #-}
|
{-# LANGUAGE CPP #-}
|
||||||
{-# LANGUAGE DeriveDataTypeable #-}
|
{-# LANGUAGE DeriveDataTypeable #-}
|
||||||
{-# LANGUAGE EmptyDataDecls #-}
|
{-# LANGUAGE EmptyDataDecls #-}
|
||||||
#if __GLASGOW_HASKELL__ >= 708
|
|
||||||
{-# LANGUAGE PatternSynonyms #-}
|
{-# LANGUAGE PatternSynonyms #-}
|
||||||
#endif
|
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
|
||||||
|
|
||||||
module HPath
|
module HPath
|
||||||
(
|
(
|
||||||
@ -25,39 +21,31 @@ module HPath
|
|||||||
Abs
|
Abs
|
||||||
,Path
|
,Path
|
||||||
,Rel
|
,Rel
|
||||||
|
,Fn
|
||||||
,PathParseException
|
,PathParseException
|
||||||
,PathException
|
,PathException
|
||||||
#if __GLASGOW_HASKELL__ >= 708
|
,RelC
|
||||||
-- * PatternSynonyms/ViewPatterns
|
-- * PatternSynonyms/ViewPatterns
|
||||||
,pattern Path
|
,pattern Path
|
||||||
#endif
|
-- * Path Parsing
|
||||||
-- * Path Construction
|
|
||||||
,parseAbs
|
,parseAbs
|
||||||
|
,parseFn
|
||||||
,parseRel
|
,parseRel
|
||||||
,parseAny
|
|
||||||
,rootPath
|
|
||||||
-- * Path Conversion
|
-- * Path Conversion
|
||||||
,fromAbs
|
,fromAbs
|
||||||
,fromRel
|
,fromRel
|
||||||
,toFilePath
|
,toFilePath
|
||||||
,fromAny
|
|
||||||
-- * Path Operations
|
-- * Path Operations
|
||||||
,(</>)
|
,(</>)
|
||||||
,basename
|
,basename
|
||||||
,dirname
|
,dirname
|
||||||
,getAllParents
|
|
||||||
,getAllComponents
|
|
||||||
,getAllComponentsAfterRoot
|
|
||||||
,stripDir
|
|
||||||
-- * Path Examination
|
|
||||||
,isParentOf
|
,isParentOf
|
||||||
,isRootPath
|
,getAllParents
|
||||||
|
,stripDir
|
||||||
-- * Path IO helpers
|
-- * Path IO helpers
|
||||||
,withAbsPath
|
,withAbsPath
|
||||||
,withRelPath
|
,withRelPath
|
||||||
-- * Quasiquoters
|
,withFnPath
|
||||||
,abs
|
|
||||||
,rel
|
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
@ -70,15 +58,10 @@ import Data.ByteString(ByteString)
|
|||||||
import qualified Data.List as L
|
import qualified Data.List as L
|
||||||
#endif
|
#endif
|
||||||
import qualified Data.ByteString as BS
|
import qualified Data.ByteString as BS
|
||||||
import Data.ByteString.UTF8
|
|
||||||
import Data.Data
|
import Data.Data
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.Word8
|
import Data.Word8
|
||||||
import HPath.Internal
|
import HPath.Internal
|
||||||
import Language.Haskell.TH
|
|
||||||
import Language.Haskell.TH.Syntax (Exp(..), Lift(..), lift)
|
|
||||||
import Language.Haskell.TH.Quote (QuasiQuoter(..))
|
|
||||||
import Prelude hiding (abs, any)
|
|
||||||
import System.Posix.FilePath hiding ((</>))
|
import System.Posix.FilePath hiding ((</>))
|
||||||
|
|
||||||
|
|
||||||
@ -91,10 +74,14 @@ data Abs deriving (Typeable)
|
|||||||
-- | A relative path; one without a root.
|
-- | A relative path; one without a root.
|
||||||
data Rel deriving (Typeable)
|
data Rel deriving (Typeable)
|
||||||
|
|
||||||
|
-- | A filename, without any '/'.
|
||||||
|
data Fn deriving (Typeable)
|
||||||
|
|
||||||
-- | Exception when parsing a location.
|
-- | Exception when parsing a location.
|
||||||
data PathParseException
|
data PathParseException
|
||||||
= InvalidAbs ByteString
|
= InvalidAbs ByteString
|
||||||
| InvalidRel ByteString
|
| InvalidRel ByteString
|
||||||
|
| InvalidFn ByteString
|
||||||
| Couldn'tStripPrefixTPS ByteString ByteString
|
| Couldn'tStripPrefixTPS ByteString ByteString
|
||||||
deriving (Show,Typeable)
|
deriving (Show,Typeable)
|
||||||
instance Exception PathParseException
|
instance Exception PathParseException
|
||||||
@ -103,6 +90,10 @@ data PathException = RootDirHasNoBasename
|
|||||||
deriving (Show,Typeable)
|
deriving (Show,Typeable)
|
||||||
instance Exception PathException
|
instance Exception PathException
|
||||||
|
|
||||||
|
class RelC m
|
||||||
|
|
||||||
|
instance RelC Rel
|
||||||
|
instance RelC Fn
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- PatternSynonyms
|
-- PatternSynonyms
|
||||||
@ -110,9 +101,7 @@ instance Exception PathException
|
|||||||
#if __GLASGOW_HASKELL__ >= 710
|
#if __GLASGOW_HASKELL__ >= 710
|
||||||
pattern Path :: ByteString -> Path a
|
pattern Path :: ByteString -> Path a
|
||||||
#endif
|
#endif
|
||||||
#if __GLASGOW_HASKELL__ >= 708
|
|
||||||
pattern Path x <- (MkPath x)
|
pattern Path x <- (MkPath x)
|
||||||
#endif
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Path Parsers
|
-- Path Parsers
|
||||||
@ -130,7 +119,7 @@ pattern Path x <- (MkPath x)
|
|||||||
-- >>> parseAbs "/abc/def" :: Maybe (Path Abs)
|
-- >>> parseAbs "/abc/def" :: Maybe (Path Abs)
|
||||||
-- Just "/abc/def"
|
-- Just "/abc/def"
|
||||||
-- >>> parseAbs "/abc/def/.///" :: Maybe (Path Abs)
|
-- >>> parseAbs "/abc/def/.///" :: Maybe (Path Abs)
|
||||||
-- Just "/abc/def"
|
-- Just "/abc/def/"
|
||||||
-- >>> parseAbs "abc" :: Maybe (Path Abs)
|
-- >>> parseAbs "abc" :: Maybe (Path Abs)
|
||||||
-- Nothing
|
-- Nothing
|
||||||
-- >>> parseAbs "" :: Maybe (Path Abs)
|
-- >>> parseAbs "" :: Maybe (Path Abs)
|
||||||
@ -143,7 +132,7 @@ parseAbs filepath =
|
|||||||
if isAbsolute filepath &&
|
if isAbsolute filepath &&
|
||||||
isValid filepath &&
|
isValid filepath &&
|
||||||
not (hasParentDir filepath)
|
not (hasParentDir filepath)
|
||||||
then return (MkPath . dropTrailingPathSeparator . normalise $ filepath)
|
then return (MkPath $ normalise filepath)
|
||||||
else throwM (InvalidAbs filepath)
|
else throwM (InvalidAbs filepath)
|
||||||
|
|
||||||
|
|
||||||
@ -158,11 +147,11 @@ parseAbs filepath =
|
|||||||
-- >>> parseRel "abc" :: Maybe (Path Rel)
|
-- >>> parseRel "abc" :: Maybe (Path Rel)
|
||||||
-- Just "abc"
|
-- Just "abc"
|
||||||
-- >>> parseRel "def/" :: Maybe (Path Rel)
|
-- >>> parseRel "def/" :: Maybe (Path Rel)
|
||||||
-- Just "def"
|
-- Just "def/"
|
||||||
-- >>> parseRel "abc/def" :: Maybe (Path Rel)
|
-- >>> parseRel "abc/def" :: Maybe (Path Rel)
|
||||||
-- Just "abc/def"
|
-- Just "abc/def"
|
||||||
-- >>> parseRel "abc/def/." :: Maybe (Path Rel)
|
-- >>> parseRel "abc/def/." :: Maybe (Path Rel)
|
||||||
-- Just "abc/def"
|
-- Just "abc/def/"
|
||||||
-- >>> parseRel "/abc" :: Maybe (Path Rel)
|
-- >>> parseRel "/abc" :: Maybe (Path Rel)
|
||||||
-- Nothing
|
-- Nothing
|
||||||
-- >>> parseRel "" :: Maybe (Path Rel)
|
-- >>> parseRel "" :: Maybe (Path Rel)
|
||||||
@ -181,46 +170,46 @@ parseRel filepath =
|
|||||||
filepath /= BS.pack [_period, _period] &&
|
filepath /= BS.pack [_period, _period] &&
|
||||||
not (hasParentDir filepath) &&
|
not (hasParentDir filepath) &&
|
||||||
isValid filepath
|
isValid filepath
|
||||||
then return (MkPath . dropTrailingPathSeparator . normalise $ filepath)
|
then return (MkPath $ normalise filepath)
|
||||||
else throwM (InvalidRel filepath)
|
else throwM (InvalidRel filepath)
|
||||||
|
|
||||||
|
|
||||||
|
-- | Parses a filename. Filenames must not contain slashes.
|
||||||
-- | Parses a path, whether it's relative or absolute.
|
|
||||||
--
|
|
||||||
-- Excludes '.' and '..'.
|
-- Excludes '.' and '..'.
|
||||||
--
|
--
|
||||||
-- Throws: 'PathParseException'
|
-- Throws: 'PathParseException'
|
||||||
--
|
--
|
||||||
-- >>> parseAny "/abc" :: Maybe (Either (Path Abs) (Path Rel))
|
-- >>> parseFn "abc" :: Maybe (Path Fn)
|
||||||
-- Just (Left "/abc")
|
-- Just "abc"
|
||||||
-- >>> parseAny "..." :: Maybe (Either (Path Abs) (Path Rel))
|
-- >>> parseFn "..." :: Maybe (Path Fn)
|
||||||
-- Just (Right "...")
|
-- Just "..."
|
||||||
-- >>> parseAny "abc/def" :: Maybe (Either (Path Abs) (Path Rel))
|
-- >>> parseFn "def/" :: Maybe (Path Fn)
|
||||||
-- Just (Right "abc/def")
|
|
||||||
-- >>> parseAny "abc/def/." :: Maybe (Either (Path Abs) (Path Rel))
|
|
||||||
-- Just (Right "abc/def")
|
|
||||||
-- >>> parseAny "/abc" :: Maybe (Either (Path Abs) (Path Rel))
|
|
||||||
-- Just (Left "/abc")
|
|
||||||
-- >>> parseAny "" :: Maybe (Either (Path Abs) (Path Rel))
|
|
||||||
-- Nothing
|
-- Nothing
|
||||||
-- >>> parseAny "abc/../foo" :: Maybe (Either (Path Abs) (Path Rel))
|
-- >>> parseFn "abc/def" :: Maybe (Path Fn)
|
||||||
-- Nothing
|
-- Nothing
|
||||||
-- >>> parseAny "." :: Maybe (Either (Path Abs) (Path Rel))
|
-- >>> parseFn "abc/def/." :: Maybe (Path Fn)
|
||||||
-- Nothing
|
-- Nothing
|
||||||
-- >>> parseAny ".." :: Maybe (Either (Path Abs) (Path Rel))
|
-- >>> parseFn "/abc" :: Maybe (Path Fn)
|
||||||
-- Nothing
|
-- Nothing
|
||||||
parseAny :: MonadThrow m => ByteString -> m (Either (Path Abs) (Path Rel))
|
-- >>> parseFn "" :: Maybe (Path Fn)
|
||||||
parseAny filepath = case parseAbs filepath of
|
-- Nothing
|
||||||
Just p -> pure $ Left p
|
-- >>> parseFn "abc/../foo" :: Maybe (Path Fn)
|
||||||
Nothing -> case parseRel filepath of
|
-- Nothing
|
||||||
Just p -> pure $ Right p
|
-- >>> parseFn "." :: Maybe (Path Fn)
|
||||||
Nothing -> throwM (InvalidRel filepath)
|
-- Nothing
|
||||||
|
-- >>> parseFn ".." :: Maybe (Path Fn)
|
||||||
|
-- Nothing
|
||||||
|
parseFn :: MonadThrow m
|
||||||
|
=> ByteString -> m (Path Fn)
|
||||||
|
parseFn filepath =
|
||||||
|
if isFileName filepath &&
|
||||||
|
filepath /= BS.singleton _period &&
|
||||||
|
filepath /= BS.pack [_period, _period] &&
|
||||||
|
isValid filepath
|
||||||
|
then return (MkPath filepath)
|
||||||
|
else throwM (InvalidFn filepath)
|
||||||
|
|
||||||
|
|
||||||
rootPath :: Path Abs
|
|
||||||
rootPath = (MkPath (BS.singleton _slash))
|
|
||||||
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Path Conversion
|
-- Path Conversion
|
||||||
@ -234,11 +223,9 @@ fromAbs :: Path Abs -> ByteString
|
|||||||
fromAbs = toFilePath
|
fromAbs = toFilePath
|
||||||
|
|
||||||
-- | Convert a relative Path to a ByteString type.
|
-- | Convert a relative Path to a ByteString type.
|
||||||
fromRel :: Path Rel -> ByteString
|
fromRel :: RelC r => Path r -> ByteString
|
||||||
fromRel = toFilePath
|
fromRel = toFilePath
|
||||||
|
|
||||||
fromAny :: Either (Path Abs) (Path Rel) -> ByteString
|
|
||||||
fromAny = either toFilePath toFilePath
|
|
||||||
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
@ -259,15 +246,14 @@ fromAny = either toFilePath toFilePath
|
|||||||
-- "/path/to/file"
|
-- "/path/to/file"
|
||||||
-- >>> (MkPath "/") </> (MkPath "file/lal" :: Path Rel)
|
-- >>> (MkPath "/") </> (MkPath "file/lal" :: Path Rel)
|
||||||
-- "/file/lal"
|
-- "/file/lal"
|
||||||
-- >>> (MkPath "/") </> (MkPath "file" :: Path Rel)
|
-- >>> (MkPath "/") </> (MkPath "file/" :: Path Rel)
|
||||||
-- "/file"
|
-- "/file/"
|
||||||
(</>) :: Path b -> Path Rel -> Path b
|
(</>) :: RelC r => Path b -> Path r -> Path b
|
||||||
(</>) (MkPath a) (MkPath b) = MkPath (a' `BS.append` b)
|
(</>) (MkPath a) (MkPath b) = MkPath (a' `BS.append` b)
|
||||||
where
|
where
|
||||||
a' = if hasTrailingPathSeparator a
|
a' = if BS.last a == pathSeparator
|
||||||
then a
|
then a
|
||||||
else addTrailingPathSeparator a
|
else addTrailingPathSeparator a
|
||||||
|
|
||||||
|
|
||||||
-- | Strip directory from path, making it relative to that directory.
|
-- | Strip directory from path, making it relative to that directory.
|
||||||
-- Throws 'Couldn'tStripPrefixDir' if directory is not a parent of the path.
|
-- Throws 'Couldn'tStripPrefixDir' if directory is not a parent of the path.
|
||||||
@ -295,81 +281,6 @@ stripDir (MkPath p) (MkPath l) =
|
|||||||
where
|
where
|
||||||
p' = addTrailingPathSeparator p
|
p' = addTrailingPathSeparator p
|
||||||
|
|
||||||
|
|
||||||
-- |Get all parents of a path.
|
|
||||||
--
|
|
||||||
-- >>> getAllParents (MkPath "/abs/def/dod")
|
|
||||||
-- ["/abs/def","/abs","/"]
|
|
||||||
-- >>> getAllParents (MkPath "/foo")
|
|
||||||
-- ["/"]
|
|
||||||
-- >>> getAllParents (MkPath "/")
|
|
||||||
-- []
|
|
||||||
getAllParents :: Path Abs -> [Path Abs]
|
|
||||||
getAllParents (MkPath p)
|
|
||||||
| np == BS.singleton pathSeparator = []
|
|
||||||
| otherwise = dirname (MkPath np) : getAllParents (dirname $ MkPath np)
|
|
||||||
where
|
|
||||||
np = normalise p
|
|
||||||
|
|
||||||
|
|
||||||
-- | Gets all path components.
|
|
||||||
--
|
|
||||||
-- >>> getAllComponents (MkPath "abs/def/dod")
|
|
||||||
-- ["abs","def","dod"]
|
|
||||||
-- >>> getAllComponents (MkPath "abs")
|
|
||||||
-- ["abs"]
|
|
||||||
getAllComponents :: Path Rel -> [Path Rel]
|
|
||||||
getAllComponents (MkPath p) = fmap MkPath . splitDirectories $ p
|
|
||||||
|
|
||||||
|
|
||||||
-- | Gets all path components after the "/" root directory.
|
|
||||||
--
|
|
||||||
-- >>> getAllComponentsAfterRoot (MkPath "/abs/def/dod")
|
|
||||||
-- ["abs","def","dod"]
|
|
||||||
-- >>> getAllComponentsAfterRoot (MkPath "/abs")
|
|
||||||
-- ["abs"]
|
|
||||||
getAllComponentsAfterRoot :: Path Abs -> [Path Rel]
|
|
||||||
getAllComponentsAfterRoot p = getAllComponents (fromJust $ stripDir rootPath p)
|
|
||||||
|
|
||||||
|
|
||||||
-- | Extract the directory name of a path.
|
|
||||||
--
|
|
||||||
-- >>> dirname (MkPath "/abc/def/dod")
|
|
||||||
-- "/abc/def"
|
|
||||||
-- >>> dirname (MkPath "/")
|
|
||||||
-- "/"
|
|
||||||
dirname :: Path Abs -> Path Abs
|
|
||||||
dirname (MkPath fp) = MkPath (takeDirectory fp)
|
|
||||||
|
|
||||||
-- | Extract the file part of a path.
|
|
||||||
--
|
|
||||||
--
|
|
||||||
-- The following properties hold:
|
|
||||||
--
|
|
||||||
-- @basename (p \<\/> a) == basename a@
|
|
||||||
--
|
|
||||||
-- Throws: `PathException` if given the root path "/"
|
|
||||||
--
|
|
||||||
-- >>> basename (MkPath "/abc/def/dod") :: Maybe (Path Rel)
|
|
||||||
-- Just "dod"
|
|
||||||
-- >>> basename (MkPath "abc/def/dod") :: Maybe (Path Rel)
|
|
||||||
-- Just "dod"
|
|
||||||
-- >>> basename (MkPath "dod") :: Maybe (Path Rel)
|
|
||||||
-- Just "dod"
|
|
||||||
-- >>> basename (MkPath "/") :: Maybe (Path Rel)
|
|
||||||
-- Nothing
|
|
||||||
basename :: MonadThrow m => Path b -> m (Path Rel)
|
|
||||||
basename (MkPath l)
|
|
||||||
| not (isAbsolute rl) = return $ MkPath rl
|
|
||||||
| otherwise = throwM RootDirHasNoBasename
|
|
||||||
where
|
|
||||||
rl = last . splitPath $ l
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
-- Path Examination
|
|
||||||
|
|
||||||
-- | Is p a parent of the given location? Implemented in terms of
|
-- | Is p a parent of the given location? Implemented in terms of
|
||||||
-- 'stripDir'. The bases must match.
|
-- 'stripDir'. The bases must match.
|
||||||
--
|
--
|
||||||
@ -387,14 +298,50 @@ isParentOf :: Path b -> Path b -> Bool
|
|||||||
isParentOf p l = isJust (stripDir p l :: Maybe (Path Rel))
|
isParentOf p l = isJust (stripDir p l :: Maybe (Path Rel))
|
||||||
|
|
||||||
|
|
||||||
-- | Check whether the given Path is the root "/" path.
|
-- |Get all parents of a path.
|
||||||
--
|
--
|
||||||
-- >>> isRootPath (MkPath "/lal/lad")
|
-- >>> getAllParents (MkPath "/abs/def/dod")
|
||||||
-- False
|
-- ["/abs/def","/abs","/"]
|
||||||
-- >>> isRootPath (MkPath "/")
|
-- >>> getAllParents (MkPath "/")
|
||||||
-- True
|
-- []
|
||||||
isRootPath :: Path Abs -> Bool
|
getAllParents :: Path Abs -> [Path Abs]
|
||||||
isRootPath = (== rootPath)
|
getAllParents (MkPath p)
|
||||||
|
| np == BS.singleton pathSeparator = []
|
||||||
|
| otherwise = dirname (MkPath np) : getAllParents (dirname $ MkPath np)
|
||||||
|
where
|
||||||
|
np = dropTrailingPathSeparator . normalise $ p
|
||||||
|
|
||||||
|
|
||||||
|
-- | Extract the directory name of a path.
|
||||||
|
--
|
||||||
|
-- >>> dirname (MkPath "/abc/def/dod")
|
||||||
|
-- "/abc/def"
|
||||||
|
-- >>> dirname (MkPath "/")
|
||||||
|
-- "/"
|
||||||
|
dirname :: Path Abs -> Path Abs
|
||||||
|
dirname (MkPath fp) = MkPath (takeDirectory $ dropTrailingPathSeparator fp)
|
||||||
|
|
||||||
|
-- | Extract the file part of a path.
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- The following properties hold:
|
||||||
|
--
|
||||||
|
-- @basename (p \<\/> a) == basename a@
|
||||||
|
--
|
||||||
|
-- Throws: `PathException` if given the root path "/"
|
||||||
|
--
|
||||||
|
-- >>> basename (MkPath "/abc/def/dod") :: Maybe (Path Fn)
|
||||||
|
-- Just "dod"
|
||||||
|
-- >>> basename (MkPath "/abc/def/dod/") :: Maybe (Path Fn)
|
||||||
|
-- Just "dod"
|
||||||
|
-- >>> basename (MkPath "/") :: Maybe (Path Fn)
|
||||||
|
-- Nothing
|
||||||
|
basename :: MonadThrow m => Path b -> m (Path Fn)
|
||||||
|
basename (MkPath l)
|
||||||
|
| not (isAbsolute rl) = return $ MkPath rl
|
||||||
|
| otherwise = throwM RootDirHasNoBasename
|
||||||
|
where
|
||||||
|
rl = last . splitPath . dropTrailingPathSeparator $ l
|
||||||
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
@ -409,6 +356,9 @@ withRelPath :: Path Rel -> (ByteString -> IO a) -> IO a
|
|||||||
withRelPath (MkPath p) action = action p
|
withRelPath (MkPath p) action = action p
|
||||||
|
|
||||||
|
|
||||||
|
withFnPath :: Path Fn -> (ByteString -> IO a) -> IO a
|
||||||
|
withFnPath (MkPath p) action = action p
|
||||||
|
|
||||||
|
|
||||||
------------------------
|
------------------------
|
||||||
-- ByteString helpers
|
-- ByteString helpers
|
||||||
@ -418,52 +368,3 @@ withRelPath (MkPath p) action = action p
|
|||||||
stripPrefix :: ByteString -> ByteString -> Maybe ByteString
|
stripPrefix :: ByteString -> ByteString -> Maybe ByteString
|
||||||
stripPrefix a b = BS.pack `fmap` L.stripPrefix (BS.unpack a) (BS.unpack b)
|
stripPrefix a b = BS.pack `fmap` L.stripPrefix (BS.unpack a) (BS.unpack b)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
------------------------
|
|
||||||
-- QuasiQuoters
|
|
||||||
|
|
||||||
instance Lift (Path a) where
|
|
||||||
lift (MkPath bs) = AppE <$> [| MkPath . BS.pack |] <*> lift (BS.unpack bs)
|
|
||||||
|
|
||||||
|
|
||||||
qq :: (ByteString -> Q Exp) -> QuasiQuoter
|
|
||||||
qq quoteExp' =
|
|
||||||
QuasiQuoter
|
|
||||||
{ quoteExp = (\s -> quoteExp' . fromString $ s)
|
|
||||||
, quotePat = \_ ->
|
|
||||||
fail "illegal QuasiQuote (allowed as expression only, used as a pattern)"
|
|
||||||
, quoteType = \_ ->
|
|
||||||
fail "illegal QuasiQuote (allowed as expression only, used as a type)"
|
|
||||||
, quoteDec = \_ ->
|
|
||||||
fail "illegal QuasiQuote (allowed as expression only, used as a declaration)"
|
|
||||||
}
|
|
||||||
|
|
||||||
mkAbs :: ByteString -> Q Exp
|
|
||||||
mkAbs = either (error . show) lift . parseAbs
|
|
||||||
|
|
||||||
mkRel :: ByteString -> Q Exp
|
|
||||||
mkRel = either (error . show) lift . parseRel
|
|
||||||
|
|
||||||
-- | Quasiquote an absolute Path. This accepts Unicode Chars and will encode as UTF-8.
|
|
||||||
--
|
|
||||||
-- >>> [abs|/etc/profile|] :: Path Abs
|
|
||||||
-- "/etc/profile"
|
|
||||||
-- >>> [abs|/|] :: Path Abs
|
|
||||||
-- "/"
|
|
||||||
-- >>> [abs|/|] :: Path Abs
|
|
||||||
-- "/\239\131\144"
|
|
||||||
abs :: QuasiQuoter
|
|
||||||
abs = qq mkAbs
|
|
||||||
|
|
||||||
-- | Quasiquote a relative Path. This accepts Unicode Chars and will encode as UTF-8.
|
|
||||||
--
|
|
||||||
-- >>> [rel|etc|] :: Path Rel
|
|
||||||
-- "etc"
|
|
||||||
-- >>> [rel|bar/baz|] :: Path Rel
|
|
||||||
-- "bar/baz"
|
|
||||||
-- >>> [rel||] :: Path Rel
|
|
||||||
-- "\239\131\144"
|
|
||||||
rel :: QuasiQuoter
|
|
||||||
rel = qq mkRel
|
|
||||||
|
|
1099
src/HPath/IO.hs
Normal file
1099
src/HPath/IO.hs
Normal file
File diff suppressed because it is too large
Load Diff
8
src/HPath/IO.hs-boot
Normal file
8
src/HPath/IO.hs-boot
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
module HPath.IO where
|
||||||
|
|
||||||
|
|
||||||
|
import HPath
|
||||||
|
|
||||||
|
canonicalizePath :: Path b -> IO (Path Abs)
|
||||||
|
|
||||||
|
toAbs :: Path b -> IO (Path Abs)
|
@ -1,5 +1,5 @@
|
|||||||
-- |
|
-- |
|
||||||
-- Module : System.Posix.RawFilePath.Directory.Errors
|
-- Module : HPath.IO.Errors
|
||||||
-- Copyright : © 2016 Julian Ospald
|
-- Copyright : © 2016 Julian Ospald
|
||||||
-- License : BSD3
|
-- License : BSD3
|
||||||
--
|
--
|
||||||
@ -12,7 +12,7 @@
|
|||||||
{-# LANGUAGE DeriveDataTypeable #-}
|
{-# LANGUAGE DeriveDataTypeable #-}
|
||||||
{-# LANGUAGE ScopedTypeVariables #-}
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.Errors
|
module HPath.IO.Errors
|
||||||
(
|
(
|
||||||
-- * Types
|
-- * Types
|
||||||
HPathIOException(..)
|
HPathIOException(..)
|
||||||
@ -33,12 +33,15 @@ module System.Posix.RawFilePath.Directory.Errors
|
|||||||
, throwSameFile
|
, throwSameFile
|
||||||
, sameFile
|
, sameFile
|
||||||
, throwDestinationInSource
|
, throwDestinationInSource
|
||||||
|
, doesFileExist
|
||||||
|
, doesDirectoryExist
|
||||||
|
, isWritable
|
||||||
|
, canOpenDirectory
|
||||||
|
|
||||||
-- * Error handling functions
|
-- * Error handling functions
|
||||||
, catchErrno
|
, catchErrno
|
||||||
, rethrowErrnoAs
|
, rethrowErrnoAs
|
||||||
, handleIOError
|
, handleIOError
|
||||||
, hideError
|
|
||||||
, bracketeer
|
, bracketeer
|
||||||
, reactOnError
|
, reactOnError
|
||||||
)
|
)
|
||||||
@ -49,7 +52,7 @@ import Control.Applicative
|
|||||||
(
|
(
|
||||||
(<$>)
|
(<$>)
|
||||||
)
|
)
|
||||||
import Control.Exception.Safe hiding (handleIOError)
|
import Control.Exception
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
(
|
(
|
||||||
forM
|
forM
|
||||||
@ -63,7 +66,6 @@ import Data.ByteString
|
|||||||
(
|
(
|
||||||
ByteString
|
ByteString
|
||||||
)
|
)
|
||||||
import qualified Data.ByteString as BS
|
|
||||||
import Data.ByteString.UTF8
|
import Data.ByteString.UTF8
|
||||||
(
|
(
|
||||||
toString
|
toString
|
||||||
@ -81,22 +83,24 @@ import GHC.IO.Exception
|
|||||||
(
|
(
|
||||||
IOErrorType
|
IOErrorType
|
||||||
)
|
)
|
||||||
import {-# SOURCE #-} System.Posix.RawFilePath.Directory
|
import HPath
|
||||||
|
import HPath.Internal
|
||||||
|
(
|
||||||
|
Path(..)
|
||||||
|
)
|
||||||
|
import {-# SOURCE #-} HPath.IO
|
||||||
(
|
(
|
||||||
canonicalizePath
|
canonicalizePath
|
||||||
, toAbs
|
, toAbs
|
||||||
, doesFileExist
|
|
||||||
, doesDirectoryExist
|
|
||||||
, isWritable
|
|
||||||
, canOpenDirectory
|
|
||||||
)
|
)
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
alreadyExistsErrorType
|
alreadyExistsErrorType
|
||||||
|
, catchIOError
|
||||||
, ioeGetErrorType
|
, ioeGetErrorType
|
||||||
, mkIOError
|
, mkIOError
|
||||||
)
|
)
|
||||||
import System.Posix.FilePath
|
|
||||||
import qualified System.Posix.Directory.ByteString as PFD
|
import qualified System.Posix.Directory.ByteString as PFD
|
||||||
import System.Posix.Files.ByteString
|
import System.Posix.Files.ByteString
|
||||||
(
|
(
|
||||||
@ -145,9 +149,9 @@ toConstr RecursiveFailure {} = "RecursiveFailure"
|
|||||||
|
|
||||||
|
|
||||||
isSameFile, isDestinationInSource, isRecursiveFailure :: HPathIOException -> Bool
|
isSameFile, isDestinationInSource, isRecursiveFailure :: HPathIOException -> Bool
|
||||||
isSameFile ex = toConstr (ex :: HPathIOException) == toConstr (SameFile mempty mempty)
|
isSameFile ex = toConstr (ex :: HPathIOException) == toConstr SameFile{}
|
||||||
isDestinationInSource ex = toConstr (ex :: HPathIOException) == (toConstr $ DestinationInSource mempty mempty)
|
isDestinationInSource ex = toConstr (ex :: HPathIOException) == toConstr DestinationInSource{}
|
||||||
isRecursiveFailure ex = toConstr (ex :: HPathIOException) == (toConstr $ RecursiveFailure mempty)
|
isRecursiveFailure ex = toConstr (ex :: HPathIOException) == toConstr RecursiveFailure{}
|
||||||
|
|
||||||
|
|
||||||
isReadContentsFailed, isCreateDirFailed, isCopyFileFailed, isRecreateSymlinkFailed ::RecursiveFailureHint -> Bool
|
isReadContentsFailed, isCreateDirFailed, isCopyFileFailed, isRecreateSymlinkFailed ::RecursiveFailureHint -> Bool
|
||||||
@ -170,9 +174,9 @@ isRecreateSymlinkFailed _ = False
|
|||||||
|
|
||||||
|
|
||||||
-- |Throws `AlreadyExists` `IOError` if file exists.
|
-- |Throws `AlreadyExists` `IOError` if file exists.
|
||||||
throwFileDoesExist :: RawFilePath -> IO ()
|
throwFileDoesExist :: Path b -> IO ()
|
||||||
throwFileDoesExist bs =
|
throwFileDoesExist fp@(MkPath bs) =
|
||||||
whenM (doesFileExist bs)
|
whenM (doesFileExist fp)
|
||||||
(ioError . mkIOError
|
(ioError . mkIOError
|
||||||
alreadyExistsErrorType
|
alreadyExistsErrorType
|
||||||
"File already exists"
|
"File already exists"
|
||||||
@ -182,9 +186,9 @@ throwFileDoesExist bs =
|
|||||||
|
|
||||||
|
|
||||||
-- |Throws `AlreadyExists` `IOError` if directory exists.
|
-- |Throws `AlreadyExists` `IOError` if directory exists.
|
||||||
throwDirDoesExist :: RawFilePath -> IO ()
|
throwDirDoesExist :: Path b -> IO ()
|
||||||
throwDirDoesExist bs =
|
throwDirDoesExist fp@(MkPath bs) =
|
||||||
whenM (doesDirectoryExist bs)
|
whenM (doesDirectoryExist fp)
|
||||||
(ioError . mkIOError
|
(ioError . mkIOError
|
||||||
alreadyExistsErrorType
|
alreadyExistsErrorType
|
||||||
"Directory already exists"
|
"Directory already exists"
|
||||||
@ -194,18 +198,18 @@ throwDirDoesExist bs =
|
|||||||
|
|
||||||
|
|
||||||
-- |Uses `isSameFile` and throws `SameFile` if it returns True.
|
-- |Uses `isSameFile` and throws `SameFile` if it returns True.
|
||||||
throwSameFile :: RawFilePath
|
throwSameFile :: Path b1
|
||||||
-> RawFilePath
|
-> Path b2
|
||||||
-> IO ()
|
-> IO ()
|
||||||
throwSameFile bs1 bs2 =
|
throwSameFile fp1@(MkPath bs1) fp2@(MkPath bs2) =
|
||||||
whenM (sameFile bs1 bs2)
|
whenM (sameFile fp1 fp2)
|
||||||
(throwIO $ SameFile bs1 bs2)
|
(throwIO $ SameFile bs1 bs2)
|
||||||
|
|
||||||
|
|
||||||
-- |Check if the files are the same by examining device and file id.
|
-- |Check if the files are the same by examining device and file id.
|
||||||
-- This follows symbolic links.
|
-- This follows symbolic links.
|
||||||
sameFile :: RawFilePath -> RawFilePath -> IO Bool
|
sameFile :: Path b1 -> Path b2 -> IO Bool
|
||||||
sameFile fp1 fp2 =
|
sameFile (MkPath fp1) (MkPath fp2) =
|
||||||
handleIOError (\_ -> return False) $ do
|
handleIOError (\_ -> return False) $ do
|
||||||
fs1 <- getFileStatus fp1
|
fs1 <- getFileStatus fp1
|
||||||
fs2 <- getFileStatus fp2
|
fs2 <- getFileStatus fp2
|
||||||
@ -221,24 +225,58 @@ sameFile fp1 fp2 =
|
|||||||
-- within the source directory by comparing the device+file ID of the
|
-- within the source directory by comparing the device+file ID of the
|
||||||
-- source directory with all device+file IDs of the parent directories
|
-- source directory with all device+file IDs of the parent directories
|
||||||
-- of the destination.
|
-- of the destination.
|
||||||
throwDestinationInSource :: RawFilePath -- ^ source dir
|
throwDestinationInSource :: Path b1 -- ^ source dir
|
||||||
-> RawFilePath -- ^ full destination, @dirname dest@
|
-> Path b2 -- ^ full destination, @dirname dest@
|
||||||
-- must exist
|
-- must exist
|
||||||
-> IO ()
|
-> IO ()
|
||||||
throwDestinationInSource sbs dbs = do
|
throwDestinationInSource (MkPath sbs) dest@(MkPath dbs) = do
|
||||||
destAbs <- toAbs dbs
|
destAbs <- toAbs dest
|
||||||
dest' <- (\x -> maybe x (\y -> x </> y) $ basename dbs)
|
dest' <- (\x -> maybe x (\y -> x </> y) $ basename dest)
|
||||||
<$> (canonicalizePath $ takeDirectory destAbs)
|
<$> (canonicalizePath $ dirname destAbs)
|
||||||
dids <- forM (takeAllParents dest') $ \p -> do
|
dids <- forM (getAllParents dest') $ \p -> do
|
||||||
fs <- PF.getSymbolicLinkStatus p
|
fs <- PF.getSymbolicLinkStatus (fromAbs p)
|
||||||
return (PF.deviceID fs, PF.fileID fs)
|
return (PF.deviceID fs, PF.fileID fs)
|
||||||
sid <- fmap (\x -> (PF.deviceID x, PF.fileID x))
|
sid <- fmap (\x -> (PF.deviceID x, PF.fileID x))
|
||||||
$ PF.getFileStatus sbs
|
$ PF.getFileStatus sbs
|
||||||
when (elem sid dids)
|
when (elem sid dids)
|
||||||
(throwIO $ DestinationInSource dbs sbs)
|
(throwIO $ DestinationInSource dbs sbs)
|
||||||
where
|
|
||||||
basename x = let b = takeBaseName x
|
|
||||||
in if BS.null b then Nothing else Just b
|
-- |Checks if the given file exists and is not a directory.
|
||||||
|
-- Does not follow symlinks.
|
||||||
|
doesFileExist :: Path b -> IO Bool
|
||||||
|
doesFileExist (MkPath bs) =
|
||||||
|
handleIOError (\_ -> return False) $ do
|
||||||
|
fs <- PF.getSymbolicLinkStatus bs
|
||||||
|
return $ not . PF.isDirectory $ fs
|
||||||
|
|
||||||
|
|
||||||
|
-- |Checks if the given file exists and is a directory.
|
||||||
|
-- Does not follow symlinks.
|
||||||
|
doesDirectoryExist :: Path b -> IO Bool
|
||||||
|
doesDirectoryExist (MkPath bs) =
|
||||||
|
handleIOError (\_ -> return False) $ do
|
||||||
|
fs <- PF.getSymbolicLinkStatus bs
|
||||||
|
return $ PF.isDirectory fs
|
||||||
|
|
||||||
|
|
||||||
|
-- |Checks whether a file or folder is writable.
|
||||||
|
isWritable :: Path b -> IO Bool
|
||||||
|
isWritable (MkPath bs) =
|
||||||
|
handleIOError (\_ -> return False) $
|
||||||
|
fileAccess bs False True False
|
||||||
|
|
||||||
|
|
||||||
|
-- |Checks whether the directory at the given path exists and can be
|
||||||
|
-- opened. This invokes `openDirStream` which follows symlinks.
|
||||||
|
canOpenDirectory :: Path b -> IO Bool
|
||||||
|
canOpenDirectory (MkPath bs) =
|
||||||
|
handleIOError (\_ -> return False) $ do
|
||||||
|
bracket (PFD.openDirStream bs)
|
||||||
|
PFD.closeDirStream
|
||||||
|
(\_ -> return ())
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -280,13 +318,9 @@ handleIOError :: (IOError -> IO a) -> IO a -> IO a
|
|||||||
handleIOError = flip catchIOError
|
handleIOError = flip catchIOError
|
||||||
|
|
||||||
|
|
||||||
hideError :: IOErrorType -> IO () -> IO ()
|
|
||||||
hideError err = handleIO (\e -> if err == ioeGetErrorType e then pure () else ioError e)
|
|
||||||
|
|
||||||
|
|
||||||
-- |Like `bracket`, but allows to have different clean-up
|
-- |Like `bracket`, but allows to have different clean-up
|
||||||
-- actions depending on whether the in-between computation
|
-- actions depending on whether the in-between computation
|
||||||
-- has raised an exception or not.
|
-- has raised an exception or not.
|
||||||
bracketeer :: IO a -- ^ computation to run first
|
bracketeer :: IO a -- ^ computation to run first
|
||||||
-> (a -> IO b) -- ^ computation to run last, when
|
-> (a -> IO b) -- ^ computation to run last, when
|
||||||
-- no exception was raised
|
-- no exception was raised
|
||||||
@ -324,4 +358,3 @@ reactOnError a ios fmios =
|
|||||||
(throwIO ex)
|
(throwIO ex)
|
||||||
fmios
|
fmios
|
||||||
|
|
||||||
|
|
@ -10,19 +10,15 @@ import Control.DeepSeq (NFData (..))
|
|||||||
import Data.ByteString (ByteString)
|
import Data.ByteString (ByteString)
|
||||||
import Data.Data
|
import Data.Data
|
||||||
|
|
||||||
-- | The main Path type.
|
-- | Path of some base and type.
|
||||||
--
|
--
|
||||||
-- The type variable 'b' is either:
|
-- Internally is a ByteString. The ByteString can be of two formats only:
|
||||||
--
|
--
|
||||||
-- * Abs -- absolute path
|
-- 1. without trailing path separator: @file.txt@, @foo\/bar.txt@, @\/foo\/bar.txt@
|
||||||
-- * Rel -- relative path
|
-- 2. with trailing path separator: @foo\/@, @\/foo\/bar\/@
|
||||||
--
|
--
|
||||||
-- Internally is a ByteString. The path is guaranteed to
|
-- There are no duplicate
|
||||||
-- be normalised and contain no trailing Path separators,
|
-- path separators @\/\/@, no @..@, no @.\/@, no @~\/@, etc.
|
||||||
-- except for the '/' root path.
|
|
||||||
--
|
|
||||||
-- There are no duplicate path separators
|
|
||||||
-- @\/\/@, no @..@, no @.\/@, no @~\/@, etc.
|
|
||||||
data Path b = MkPath ByteString
|
data Path b = MkPath ByteString
|
||||||
deriving (Typeable)
|
deriving (Typeable)
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
module System.Posix.Foreign where
|
module System.Posix.Directory.Foreign where
|
||||||
|
|
||||||
import Data.Bits
|
import Data.Bits
|
||||||
import Data.List (foldl')
|
import Data.List (foldl')
|
@ -1,5 +1,5 @@
|
|||||||
-- |
|
-- |
|
||||||
-- Module : System.Posix.RawFilePath.Directory.Traversals
|
-- Module : System.Posix.Directory.Traversals
|
||||||
-- Copyright : © 2016 Julian Ospald
|
-- Copyright : © 2016 Julian Ospald
|
||||||
-- License : BSD3
|
-- License : BSD3
|
||||||
--
|
--
|
||||||
@ -10,17 +10,17 @@
|
|||||||
-- Traversal and read operations on directories.
|
-- Traversal and read operations on directories.
|
||||||
|
|
||||||
|
|
||||||
{-# LANGUAGE CApiFFI #-}
|
|
||||||
{-# LANGUAGE CPP #-}
|
{-# LANGUAGE CPP #-}
|
||||||
{-# LANGUAGE ForeignFunctionInterface #-}
|
{-# LANGUAGE ForeignFunctionInterface #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE PackageImports #-}
|
||||||
{-# LANGUAGE TupleSections #-}
|
{-# LANGUAGE TupleSections #-}
|
||||||
{-# LANGUAGE ViewPatterns #-}
|
{-# LANGUAGE ViewPatterns #-}
|
||||||
|
|
||||||
{-# OPTIONS_GHC -Wall #-}
|
{-# OPTIONS_GHC -Wall #-}
|
||||||
|
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.Traversals (
|
module System.Posix.Directory.Traversals (
|
||||||
|
|
||||||
getDirectoryContents
|
getDirectoryContents
|
||||||
, getDirectoryContents'
|
, getDirectoryContents'
|
||||||
@ -44,7 +44,7 @@ import Control.Applicative ((<$>))
|
|||||||
#endif
|
#endif
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
import System.Posix.FilePath ((</>))
|
import System.Posix.FilePath ((</>))
|
||||||
import System.Posix.Foreign
|
import System.Posix.Directory.Foreign
|
||||||
|
|
||||||
import qualified System.Posix as Posix
|
import qualified System.Posix as Posix
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
@ -174,15 +174,11 @@ foreign import ccall unsafe "__hscore_d_name"
|
|||||||
foreign import ccall unsafe "__posixdir_d_type"
|
foreign import ccall unsafe "__posixdir_d_type"
|
||||||
c_type :: Ptr CDirent -> IO DirType
|
c_type :: Ptr CDirent -> IO DirType
|
||||||
|
|
||||||
foreign import capi "stdlib.h realpath"
|
foreign import ccall "realpath"
|
||||||
c_realpath :: CString -> CString -> IO CString
|
c_realpath :: CString -> CString -> IO CString
|
||||||
|
|
||||||
-- Using normal 'ccall' here lead to memory bugs, crashes
|
foreign import ccall unsafe "fdopendir"
|
||||||
-- and corrupted d_name entries. It appears there are two fdopendirs:
|
c_fdopendir :: Posix.Fd -> IO (Ptr ())
|
||||||
-- https://opensource.apple.com/source/Libc/Libc-1244.1.7/include/dirent.h.auto.html
|
|
||||||
-- The capi call picks the correct one.
|
|
||||||
foreign import capi unsafe "dirent.h fdopendir"
|
|
||||||
c_fdopendir :: Posix.Fd -> IO (Ptr CDir)
|
|
||||||
|
|
||||||
----------------------------------------------------------
|
----------------------------------------------------------
|
||||||
-- less dodgy but still lower-level
|
-- less dodgy but still lower-level
|
||||||
@ -220,7 +216,7 @@ readDirEnt (unpackDirStream -> dirp) =
|
|||||||
getDirectoryContents :: RawFilePath -> IO [(DirType, RawFilePath)]
|
getDirectoryContents :: RawFilePath -> IO [(DirType, RawFilePath)]
|
||||||
getDirectoryContents path =
|
getDirectoryContents path =
|
||||||
modifyIOError ((`ioeSetFileName` (BS.unpack path)) .
|
modifyIOError ((`ioeSetFileName` (BS.unpack path)) .
|
||||||
(`ioeSetLocation` "System.Posix.RawFilePath.Directory.Traversals.getDirectoryContents")) $
|
(`ioeSetLocation` "System.Posix.Directory.Traversals.getDirectoryContents")) $
|
||||||
bracket
|
bracket
|
||||||
(PosixBS.openDirStream path)
|
(PosixBS.openDirStream path)
|
||||||
PosixBS.closeDirStream
|
PosixBS.closeDirStream
|
@ -26,7 +26,7 @@ module System.Posix.FD (
|
|||||||
|
|
||||||
import Foreign.C.String
|
import Foreign.C.String
|
||||||
import Foreign.C.Types
|
import Foreign.C.Types
|
||||||
import System.Posix.Foreign
|
import System.Posix.Directory.Foreign
|
||||||
import qualified System.Posix as Posix
|
import qualified System.Posix as Posix
|
||||||
import System.Posix.ByteString.FilePath
|
import System.Posix.ByteString.FilePath
|
||||||
|
|
@ -59,7 +59,6 @@ module System.Posix.FilePath (
|
|||||||
, splitPath
|
, splitPath
|
||||||
, joinPath
|
, joinPath
|
||||||
, splitDirectories
|
, splitDirectories
|
||||||
, takeAllParents
|
|
||||||
|
|
||||||
-- * Trailing slash functions
|
-- * Trailing slash functions
|
||||||
, hasTrailingPathSeparator
|
, hasTrailingPathSeparator
|
||||||
@ -74,7 +73,6 @@ module System.Posix.FilePath (
|
|||||||
, isAbsolute
|
, isAbsolute
|
||||||
, isValid
|
, isValid
|
||||||
, makeValid
|
, makeValid
|
||||||
, isSpecialDirectoryEntry
|
|
||||||
, isFileName
|
, isFileName
|
||||||
, hasParentDir
|
, hasParentDir
|
||||||
, hiddenFile
|
, hiddenFile
|
||||||
@ -98,7 +96,6 @@ import Control.Arrow (second)
|
|||||||
-- $setup
|
-- $setup
|
||||||
-- >>> import Data.Char
|
-- >>> import Data.Char
|
||||||
-- >>> import Data.Maybe
|
-- >>> import Data.Maybe
|
||||||
-- >>> import Data.Word8
|
|
||||||
-- >>> import Test.QuickCheck
|
-- >>> import Test.QuickCheck
|
||||||
-- >>> import Control.Applicative
|
-- >>> import Control.Applicative
|
||||||
-- >>> import qualified Data.ByteString as BS
|
-- >>> import qualified Data.ByteString as BS
|
||||||
@ -487,8 +484,6 @@ joinPath = foldr (</>) BS.empty
|
|||||||
--
|
--
|
||||||
-- >>> splitDirectories "/path/to/file.txt"
|
-- >>> splitDirectories "/path/to/file.txt"
|
||||||
-- ["/","path","to","file.txt"]
|
-- ["/","path","to","file.txt"]
|
||||||
-- >>> splitDirectories "path/to/file.txt"
|
|
||||||
-- ["path","to","file.txt"]
|
|
||||||
-- >>> splitDirectories ""
|
-- >>> splitDirectories ""
|
||||||
-- []
|
-- []
|
||||||
splitDirectories :: RawFilePath -> [RawFilePath]
|
splitDirectories :: RawFilePath -> [RawFilePath]
|
||||||
@ -501,21 +496,6 @@ splitDirectories x
|
|||||||
splitter = filter (not . BS.null) . BS.split pathSeparator
|
splitter = filter (not . BS.null) . BS.split pathSeparator
|
||||||
|
|
||||||
|
|
||||||
-- |Get all parents of a path.
|
|
||||||
--
|
|
||||||
-- >>> takeAllParents "/abs/def/dod"
|
|
||||||
-- ["/abs/def","/abs","/"]
|
|
||||||
-- >>> takeAllParents "/foo"
|
|
||||||
-- ["/"]
|
|
||||||
-- >>> takeAllParents "/"
|
|
||||||
-- []
|
|
||||||
takeAllParents :: RawFilePath -> [RawFilePath]
|
|
||||||
takeAllParents p
|
|
||||||
| np == BS.singleton pathSeparator = []
|
|
||||||
| otherwise = takeDirectory np : takeAllParents (takeDirectory np)
|
|
||||||
where
|
|
||||||
np = normalise p
|
|
||||||
|
|
||||||
|
|
||||||
------------------------
|
------------------------
|
||||||
-- Trailing slash functions
|
-- Trailing slash functions
|
||||||
@ -743,22 +723,6 @@ makeValid path
|
|||||||
| otherwise = BS.map (\x -> if x == _nul then _underscore else x) path
|
| otherwise = BS.map (\x -> if x == _nul then _underscore else x) path
|
||||||
|
|
||||||
|
|
||||||
-- | Whether the filename is a special directory entry
|
|
||||||
-- (. and ..). Does not normalise filepaths.
|
|
||||||
--
|
|
||||||
-- >>> isSpecialDirectoryEntry "."
|
|
||||||
-- True
|
|
||||||
-- >>> isSpecialDirectoryEntry ".."
|
|
||||||
-- True
|
|
||||||
-- >>> isSpecialDirectoryEntry "/random_ path:*"
|
|
||||||
-- False
|
|
||||||
isSpecialDirectoryEntry :: RawFilePath -> Bool
|
|
||||||
isSpecialDirectoryEntry filepath
|
|
||||||
| BS.pack [_period, _period] == filepath = True
|
|
||||||
| BS.pack [_period] == filepath = True
|
|
||||||
| otherwise = False
|
|
||||||
|
|
||||||
|
|
||||||
-- | Is the given path a valid filename? This includes
|
-- | Is the given path a valid filename? This includes
|
||||||
-- "." and "..".
|
-- "." and "..".
|
||||||
--
|
--
|
@ -1,7 +1,7 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.AppendFileSpec where
|
module HPath.IO.AppendFileSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
@ -13,6 +13,7 @@ import GHC.IO.Exception
|
|||||||
(
|
(
|
||||||
IOErrorType(..)
|
IOErrorType(..)
|
||||||
)
|
)
|
||||||
|
import System.Process
|
||||||
import Utils
|
import Utils
|
||||||
|
|
||||||
|
|
||||||
@ -51,7 +52,7 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.appendFile" $ do
|
describe "HPath.IO.appendFile" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "appendFile file with content, everything clear" $ do
|
it "appendFile file with content, everything clear" $ do
|
@ -1,6 +1,6 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.CanonicalizePathSpec where
|
module HPath.IO.CanonicalizePathSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
@ -41,7 +41,7 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.canonicalizePath" $ do
|
describe "HPath.IO.canonicalizePath" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "canonicalizePath, all fine" $ do
|
it "canonicalizePath, all fine" $ do
|
@ -1,13 +1,13 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.CopyDirRecursiveCollectFailuresSpec where
|
module HPath.IO.CopyDirRecursiveCollectFailuresSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import Data.List (sort)
|
import Data.List (sort)
|
||||||
import "hpath-directory" System.Posix.RawFilePath.Directory
|
import HPath.IO
|
||||||
import System.Posix.RawFilePath.Directory.Errors
|
import HPath.IO.Errors
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
ioeGetErrorType
|
ioeGetErrorType
|
||||||
@ -116,7 +116,7 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.copyDirRecursive" $ do
|
describe "HPath.IO.copyDirRecursive" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "copyDirRecursive (Strict, CollectFailures), all fine and compare" $ do
|
it "copyDirRecursive (Strict, CollectFailures), all fine and compare" $ do
|
||||||
@ -125,10 +125,9 @@ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
|||||||
"outputDir"
|
"outputDir"
|
||||||
Strict
|
Strict
|
||||||
CollectFailures
|
CollectFailures
|
||||||
(system $ "diff -r "
|
(system $ "diff -r --no-dereference "
|
||||||
++ toString tmpDir' ++ "inputDir" ++ " "
|
++ toString tmpDir' ++ "inputDir" ++ " "
|
||||||
++ toString tmpDir' ++ "outputDir"
|
++ toString tmpDir' ++ "outputDir")
|
||||||
++ " >/dev/null")
|
|
||||||
`shouldReturn` ExitSuccess
|
`shouldReturn` ExitSuccess
|
||||||
removeDirIfExists "outputDir"
|
removeDirIfExists "outputDir"
|
||||||
|
|
@ -1,12 +1,12 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.CopyDirRecursiveOverwriteSpec where
|
module HPath.IO.CopyDirRecursiveOverwriteSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import "hpath-directory" System.Posix.RawFilePath.Directory
|
import HPath.IO
|
||||||
import System.Posix.RawFilePath.Directory.Errors
|
import HPath.IO.Errors
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
ioeGetErrorType
|
ioeGetErrorType
|
||||||
@ -88,7 +88,7 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.copyDirRecursive" $ do
|
describe "HPath.IO.copyDirRecursive" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "copyDirRecursive (Overwrite, FailEarly), all fine" $ do
|
it "copyDirRecursive (Overwrite, FailEarly), all fine" $ do
|
||||||
@ -104,28 +104,25 @@ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
|||||||
"outputDir"
|
"outputDir"
|
||||||
Overwrite
|
Overwrite
|
||||||
FailEarly
|
FailEarly
|
||||||
(system $ "diff -r "
|
(system $ "diff -r --no-dereference "
|
||||||
++ toString tmpDir' ++ "inputDir" ++ " "
|
++ toString tmpDir' ++ "inputDir" ++ " "
|
||||||
++ toString tmpDir' ++ "outputDir"
|
++ toString tmpDir' ++ "outputDir")
|
||||||
++ " >/dev/null")
|
|
||||||
`shouldReturn` ExitSuccess
|
`shouldReturn` ExitSuccess
|
||||||
removeDirIfExists "outputDir"
|
removeDirIfExists "outputDir"
|
||||||
|
|
||||||
it "copyDirRecursive (Overwrite, FailEarly), destination dir already exists" $ do
|
it "copyDirRecursive (Overwrite, FailEarly), destination dir already exists" $ do
|
||||||
tmpDir' <- getRawTmpDir
|
tmpDir' <- getRawTmpDir
|
||||||
(system $ "diff -r "
|
(system $ "diff -r --no-dereference "
|
||||||
++ toString tmpDir' ++ "inputDir" ++ " "
|
++ toString tmpDir' ++ "inputDir" ++ " "
|
||||||
++ toString tmpDir' ++ "alreadyExistsD"
|
++ toString tmpDir' ++ "alreadyExistsD")
|
||||||
++ " >/dev/null")
|
|
||||||
`shouldReturn` (ExitFailure 1)
|
`shouldReturn` (ExitFailure 1)
|
||||||
copyDirRecursive' "inputDir"
|
copyDirRecursive' "inputDir"
|
||||||
"alreadyExistsD"
|
"alreadyExistsD"
|
||||||
Overwrite
|
Overwrite
|
||||||
FailEarly
|
FailEarly
|
||||||
(system $ "diff -r "
|
(system $ "diff -r --no-dereference "
|
||||||
++ toString tmpDir' ++ "inputDir" ++ " "
|
++ toString tmpDir' ++ "inputDir" ++ " "
|
||||||
++ toString tmpDir' ++ "alreadyExistsD"
|
++ toString tmpDir' ++ "alreadyExistsD")
|
||||||
++ " >/dev/null")
|
|
||||||
`shouldReturn` ExitSuccess
|
`shouldReturn` ExitSuccess
|
||||||
removeDirIfExists "outputDir"
|
removeDirIfExists "outputDir"
|
||||||
|
|
@ -1,12 +1,12 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.CopyDirRecursiveSpec where
|
module HPath.IO.CopyDirRecursiveSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import "hpath-directory" System.Posix.RawFilePath.Directory
|
import HPath.IO
|
||||||
import System.Posix.RawFilePath.Directory.Errors
|
import HPath.IO.Errors
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
ioeGetErrorType
|
ioeGetErrorType
|
||||||
@ -73,7 +73,7 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.copyDirRecursive" $ do
|
describe "HPath.IO.copyDirRecursive" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "copyDirRecursive (Strict, FailEarly), all fine" $ do
|
it "copyDirRecursive (Strict, FailEarly), all fine" $ do
|
||||||
@ -89,10 +89,9 @@ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
|||||||
"outputDir"
|
"outputDir"
|
||||||
Strict
|
Strict
|
||||||
FailEarly
|
FailEarly
|
||||||
(system $ "diff -r "
|
(system $ "diff -r --no-dereference "
|
||||||
++ toString tmpDir' ++ "inputDir" ++ " "
|
++ toString tmpDir' ++ "inputDir" ++ " "
|
||||||
++ toString tmpDir' ++ "outputDir"
|
++ toString tmpDir' ++ "outputDir")
|
||||||
++ " >/dev/null")
|
|
||||||
`shouldReturn` ExitSuccess
|
`shouldReturn` ExitSuccess
|
||||||
removeDirIfExists "outputDir"
|
removeDirIfExists "outputDir"
|
||||||
|
|
@ -1,11 +1,11 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.CopyFileOverwriteSpec where
|
module HPath.IO.CopyFileOverwriteSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import "hpath-directory" System.Posix.RawFilePath.Directory
|
import HPath.IO
|
||||||
import System.Posix.RawFilePath.Directory.Errors
|
import HPath.IO.Errors
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
ioeGetErrorType
|
ioeGetErrorType
|
||||||
@ -59,7 +59,7 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.copyFile" $ do
|
describe "HPath.IO.copyFile" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "copyFile (Overwrite), everything clear" $ do
|
it "copyFile (Overwrite), everything clear" $ do
|
@ -1,12 +1,12 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.CopyFileSpec where
|
module HPath.IO.CopyFileSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import "hpath-directory" System.Posix.RawFilePath.Directory
|
import HPath.IO
|
||||||
import System.Posix.RawFilePath.Directory.Errors
|
import HPath.IO.Errors
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
ioeGetErrorType
|
ioeGetErrorType
|
||||||
@ -58,7 +58,7 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.copyFile" $ do
|
describe "HPath.IO.copyFile" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "copyFile (Strict), everything clear" $ do
|
it "copyFile (Strict), everything clear" $ do
|
@ -1,6 +1,6 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.CreateDirRecursiveSpec where
|
module HPath.IO.CreateDirRecursiveSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
@ -42,18 +42,13 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.createDirRecursive" $ do
|
describe "HPath.IO.createDirRecursive" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "createDirRecursive, all fine" $ do
|
it "createDirRecursive, all fine" $ do
|
||||||
createDirRecursive' "newDir"
|
createDirRecursive' "newDir"
|
||||||
deleteDir' "newDir"
|
deleteDir' "newDir"
|
||||||
|
|
||||||
it "createDirRecursive with trailing path separator, all fine" $ do
|
|
||||||
createDirRecursive' "newDir/foo/"
|
|
||||||
deleteDir' "newDir/foo"
|
|
||||||
deleteDir' "newDir"
|
|
||||||
|
|
||||||
it "createDirRecursive, parent directories do not exist" $ do
|
it "createDirRecursive, parent directories do not exist" $ do
|
||||||
createDirRecursive' "some/thing/dada"
|
createDirRecursive' "some/thing/dada"
|
||||||
deleteDir' "some/thing/dada"
|
deleteDir' "some/thing/dada"
|
@ -1,6 +1,6 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.CreateDirSpec where
|
module HPath.IO.CreateDirSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
@ -42,7 +42,7 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.createDir" $ do
|
describe "HPath.IO.createDir" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "createDir, all fine" $ do
|
it "createDir, all fine" $ do
|
@ -1,6 +1,6 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.CreateRegularFileSpec where
|
module HPath.IO.CreateRegularFileSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
@ -40,7 +40,7 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.createRegularFile" $ do
|
describe "HPath.IO.createRegularFile" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "createRegularFile, all fine" $ do
|
it "createRegularFile, all fine" $ do
|
@ -1,6 +1,6 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.CreateSymlinkSpec where
|
module HPath.IO.CreateSymlinkSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
@ -41,7 +41,7 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.createSymlink" $ do
|
describe "HPath.IO.createSymlink" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "createSymlink, all fine" $ do
|
it "createSymlink, all fine" $ do
|
@ -1,6 +1,6 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.DeleteDirRecursiveSpec where
|
module HPath.IO.DeleteDirRecursiveSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
@ -52,7 +52,7 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.deleteDirRecursive" $ do
|
describe "HPath.IO.deleteDirRecursive" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "deleteDirRecursive, empty directory, all fine" $ do
|
it "deleteDirRecursive, empty directory, all fine" $ do
|
@ -1,6 +1,6 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.DeleteDirSpec where
|
module HPath.IO.DeleteDirSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
@ -53,7 +53,7 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.deleteDir" $ do
|
describe "HPath.IO.deleteDir" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "deleteDir, empty directory, all fine" $ do
|
it "deleteDir, empty directory, all fine" $ do
|
@ -1,10 +1,10 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.DeleteFileSpec where
|
module HPath.IO.DeleteFileSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import "hpath-directory" System.Posix.RawFilePath.Directory
|
import HPath.IO
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
ioeGetErrorType
|
ioeGetErrorType
|
||||||
@ -47,7 +47,7 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.deleteFile" $ do
|
describe "HPath.IO.deleteFile" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "deleteFile, regular file, all fine" $ do
|
it "deleteFile, regular file, all fine" $ do
|
||||||
@ -70,7 +70,7 @@ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
|||||||
it "deleteFile, wrong file type (directory)" $
|
it "deleteFile, wrong file type (directory)" $
|
||||||
deleteFile' "dir"
|
deleteFile' "dir"
|
||||||
`shouldThrow`
|
`shouldThrow`
|
||||||
(\e -> ioeGetErrorType e == InappropriateType || ioeGetErrorType e == PermissionDenied)
|
(\e -> ioeGetErrorType e == InappropriateType)
|
||||||
|
|
||||||
it "deleteFile, file does not exist" $
|
it "deleteFile, file does not exist" $
|
||||||
deleteFile' "doesNotExist"
|
deleteFile' "doesNotExist"
|
@ -1,14 +1,14 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.GetDirsFilesSpec where
|
module HPath.IO.GetDirsFilesSpec where
|
||||||
|
|
||||||
|
|
||||||
import Data.List
|
import Data.List
|
||||||
(
|
(
|
||||||
sort
|
sort
|
||||||
)
|
)
|
||||||
import "hpath-directory" System.Posix.RawFilePath.Directory hiding (getDirsFiles')
|
import qualified HPath as P
|
||||||
import System.Posix.FilePath
|
import HPath.IO
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
@ -54,20 +54,20 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.getDirsFiles" $ do
|
describe "HPath.IO.getDirsFiles" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "getDirsFiles, all fine" $
|
it "getDirsFiles, all fine" $
|
||||||
withRawTmpDir $ \p -> do
|
withRawTmpDir $ \p -> do
|
||||||
let expectedFiles = [".hidden"
|
expectedFiles <- mapM P.parseRel [".hidden"
|
||||||
,"Lala"
|
,"Lala"
|
||||||
,"dir"
|
,"dir"
|
||||||
,"dirsym"
|
,"dirsym"
|
||||||
,"file"
|
,"file"
|
||||||
,"noPerms"
|
,"noPerms"
|
||||||
,"syml"]
|
,"syml"]
|
||||||
(fmap sort $ getDirsFiles p)
|
(fmap sort $ getDirsFiles p)
|
||||||
`shouldReturn` fmap (p </>) expectedFiles
|
`shouldReturn` fmap (p P.</>) expectedFiles
|
||||||
|
|
||||||
-- posix failures --
|
-- posix failures --
|
||||||
it "getDirsFiles, nonexistent directory" $
|
it "getDirsFiles, nonexistent directory" $
|
@ -1,9 +1,9 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.GetFileTypeSpec where
|
module HPath.IO.GetFileTypeSpec where
|
||||||
|
|
||||||
|
|
||||||
import "hpath-directory" System.Posix.RawFilePath.Directory
|
import HPath.IO
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
@ -48,7 +48,7 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.getFileType" $ do
|
describe "HPath.IO.getFileType" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "getFileType, regular file" $
|
it "getFileType, regular file" $
|
@ -1,11 +1,11 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.MoveFileOverwriteSpec where
|
module HPath.IO.MoveFileOverwriteSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import "hpath-directory" System.Posix.RawFilePath.Directory
|
import HPath.IO
|
||||||
import System.Posix.RawFilePath.Directory.Errors
|
import HPath.IO.Errors
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
ioeGetErrorType
|
ioeGetErrorType
|
||||||
@ -52,7 +52,7 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.moveFile" $ do
|
describe "HPath.IO.moveFile" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "moveFile (Overwrite), all fine" $
|
it "moveFile (Overwrite), all fine" $
|
@ -1,11 +1,11 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.MoveFileSpec where
|
module HPath.IO.MoveFileSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import "hpath-directory" System.Posix.RawFilePath.Directory
|
import HPath.IO
|
||||||
import System.Posix.RawFilePath.Directory.Errors
|
import HPath.IO.Errors
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
ioeGetErrorType
|
ioeGetErrorType
|
||||||
@ -54,7 +54,7 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.moveFile" $ do
|
describe "HPath.IO.moveFile" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "moveFile (Strict), all fine" $
|
it "moveFile (Strict), all fine" $
|
86
test/HPath/IO/ReadFileEOFSpec.hs
Normal file
86
test/HPath/IO/ReadFileEOFSpec.hs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
|
||||||
|
module HPath.IO.ReadFileEOFSpec where
|
||||||
|
|
||||||
|
|
||||||
|
import Test.Hspec
|
||||||
|
import System.IO.Error
|
||||||
|
(
|
||||||
|
ioeGetErrorType
|
||||||
|
)
|
||||||
|
import GHC.IO.Exception
|
||||||
|
(
|
||||||
|
IOErrorType(..)
|
||||||
|
)
|
||||||
|
import System.Process
|
||||||
|
import Utils
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
upTmpDir :: IO ()
|
||||||
|
upTmpDir = do
|
||||||
|
setTmpDir "ReadFileEOFSpec"
|
||||||
|
createTmpDir
|
||||||
|
|
||||||
|
setupFiles :: IO ()
|
||||||
|
setupFiles = do
|
||||||
|
createRegularFile' "fileWithContent"
|
||||||
|
createRegularFile' "fileWithoutContent"
|
||||||
|
createSymlink' "inputFileSymL" "fileWithContent"
|
||||||
|
createDir' "alreadyExistsD"
|
||||||
|
createRegularFile' "noPerms"
|
||||||
|
noPerms "noPerms"
|
||||||
|
createDir' "noPermsD"
|
||||||
|
createRegularFile' "noPermsD/inputFile"
|
||||||
|
noPerms "noPermsD"
|
||||||
|
writeFile' "fileWithContent" "Blahfaselgagaga"
|
||||||
|
|
||||||
|
|
||||||
|
cleanupFiles :: IO ()
|
||||||
|
cleanupFiles = do
|
||||||
|
deleteFile' "fileWithContent"
|
||||||
|
deleteFile' "fileWithoutContent"
|
||||||
|
deleteFile' "inputFileSymL"
|
||||||
|
deleteDir' "alreadyExistsD"
|
||||||
|
normalFilePerms "noPerms"
|
||||||
|
deleteFile' "noPerms"
|
||||||
|
normalDirPerms "noPermsD"
|
||||||
|
deleteFile' "noPermsD/inputFile"
|
||||||
|
deleteDir' "noPermsD"
|
||||||
|
|
||||||
|
|
||||||
|
spec :: Spec
|
||||||
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
|
describe "HPath.IO.readFileEOF" $ do
|
||||||
|
|
||||||
|
-- successes --
|
||||||
|
it "readFileEOF (Strict) file with content, everything clear" $ do
|
||||||
|
out <- readFileEOF' "fileWithContent"
|
||||||
|
out `shouldBe` "Blahfaselgagaga"
|
||||||
|
|
||||||
|
it "readFileEOF (Strict) symlink, everything clear" $ do
|
||||||
|
out <- readFileEOF' "inputFileSymL"
|
||||||
|
out `shouldBe` "Blahfaselgagaga"
|
||||||
|
|
||||||
|
it "readFileEOF (Strict) empty file, everything clear" $ do
|
||||||
|
out <- readFileEOF' "fileWithoutContent"
|
||||||
|
out `shouldBe` ""
|
||||||
|
|
||||||
|
|
||||||
|
-- posix failures --
|
||||||
|
it "readFileEOF (Strict) directory, wrong file type" $ do
|
||||||
|
readFileEOF' "alreadyExistsD"
|
||||||
|
`shouldThrow` (\e -> ioeGetErrorType e == InappropriateType)
|
||||||
|
|
||||||
|
it "readFileEOF (Strict) file, no permissions" $ do
|
||||||
|
readFileEOF' "noPerms"
|
||||||
|
`shouldThrow` (\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
|
it "readFileEOF (Strict) file, no permissions on dir" $ do
|
||||||
|
readFileEOF' "noPermsD/inputFile"
|
||||||
|
`shouldThrow` (\e -> ioeGetErrorType e == PermissionDenied)
|
||||||
|
|
||||||
|
it "readFileEOF (Strict) file, no such file" $ do
|
||||||
|
readFileEOF' "lalala"
|
||||||
|
`shouldThrow` (\e -> ioeGetErrorType e == NoSuchThing)
|
@ -1,7 +1,7 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.ReadFileSpec where
|
module HPath.IO.ReadFileSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
@ -13,6 +13,7 @@ import GHC.IO.Exception
|
|||||||
(
|
(
|
||||||
IOErrorType(..)
|
IOErrorType(..)
|
||||||
)
|
)
|
||||||
|
import System.Process
|
||||||
import Utils
|
import Utils
|
||||||
|
|
||||||
|
|
||||||
@ -51,7 +52,7 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.readFile" $ do
|
describe "HPath.IO.readFile" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "readFile (Strict) file with content, everything clear" $ do
|
it "readFile (Strict) file with content, everything clear" $ do
|
@ -1,14 +1,14 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.RecreateSymlinkOverwriteSpec where
|
module HPath.IO.RecreateSymlinkOverwriteSpec where
|
||||||
|
|
||||||
|
|
||||||
-- TODO: exception if destination exists but is not a file + `OverWrite` CopyMode
|
-- TODO: exception if destination exists but is not a file + `OverWrite` CopyMode
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import "hpath-directory" System.Posix.RawFilePath.Directory
|
import HPath.IO
|
||||||
import System.Posix.RawFilePath.Directory.Errors
|
import HPath.IO.Errors
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
ioeGetErrorType
|
ioeGetErrorType
|
||||||
@ -59,7 +59,7 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.recreateSymlink" $ do
|
describe "HPath.IO.recreateSymlink" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "recreateSymLink (Overwrite), all fine" $ do
|
it "recreateSymLink (Overwrite), all fine" $ do
|
@ -1,13 +1,13 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.RecreateSymlinkSpec where
|
module HPath.IO.RecreateSymlinkSpec where
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import "hpath-directory" System.Posix.RawFilePath.Directory
|
import HPath.IO
|
||||||
import System.Posix.RawFilePath.Directory.Errors
|
import HPath.IO.Errors
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
ioeGetErrorType
|
ioeGetErrorType
|
||||||
@ -55,7 +55,7 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.recreateSymlink" $ do
|
describe "HPath.IO.recreateSymlink" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "recreateSymLink (Strict), all fine" $ do
|
it "recreateSymLink (Strict), all fine" $ do
|
@ -1,10 +1,10 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.RenameFileSpec where
|
module HPath.IO.RenameFileSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import System.Posix.RawFilePath.Directory.Errors
|
import HPath.IO.Errors
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
ioeGetErrorType
|
ioeGetErrorType
|
||||||
@ -52,7 +52,7 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.renameFile" $ do
|
describe "HPath.IO.renameFile" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "renameFile, all fine" $
|
it "renameFile, all fine" $
|
@ -1,25 +1,26 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.ToAbsSpec where
|
module HPath.IO.ToAbsSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import "hpath-directory" System.Posix.RawFilePath.Directory
|
import HPath
|
||||||
|
import HPath.IO
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = describe "System.Posix.RawFilePath.Directory.toAbs" $ do
|
spec = describe "HPath.IO.toAbs" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "toAbs returns absolute paths unchanged" $ do
|
it "toAbs returns absolute paths unchanged" $ do
|
||||||
let p1 = "/a/b/c/d"
|
p1 <- parseAbs "/a/b/c/d"
|
||||||
to <- toAbs p1
|
to <- toAbs p1
|
||||||
p1 `shouldBe` to
|
p1 `shouldBe` to
|
||||||
|
|
||||||
it "toAbs returns even existing absolute paths unchanged" $ do
|
it "toAbs returns even existing absolute paths unchanged" $ do
|
||||||
let p1 = "/home"
|
p1 <- parseAbs "/home"
|
||||||
to <- toAbs p1
|
to <- toAbs p1
|
||||||
p1 `shouldBe` to
|
p1 `shouldBe` to
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
|
||||||
module System.Posix.RawFilePath.Directory.WriteFileSpec where
|
module HPath.IO.WriteFileSpec where
|
||||||
|
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
@ -13,6 +13,7 @@ import GHC.IO.Exception
|
|||||||
(
|
(
|
||||||
IOErrorType(..)
|
IOErrorType(..)
|
||||||
)
|
)
|
||||||
|
import System.Process
|
||||||
import Utils
|
import Utils
|
||||||
|
|
||||||
|
|
||||||
@ -51,7 +52,7 @@ cleanupFiles = do
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||||
describe "System.Posix.RawFilePath.Directory.writeFile" $ do
|
describe "HPath.IO.writeFile" $ do
|
||||||
|
|
||||||
-- successes --
|
-- successes --
|
||||||
it "writeFile file with content, everything clear" $ do
|
it "writeFile file with content, everything clear" $ do
|
19
test/Main.hs
Normal file
19
test/Main.hs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
import Test.Hspec
|
||||||
|
import Test.Hspec.Runner
|
||||||
|
import Test.Hspec.Formatters
|
||||||
|
import qualified Spec
|
||||||
|
import Utils
|
||||||
|
|
||||||
|
|
||||||
|
-- TODO: chardev, blockdev, namedpipe, socket
|
||||||
|
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main =
|
||||||
|
hspecWith
|
||||||
|
defaultConfig { configFormatter = Just progress }
|
||||||
|
$ beforeAll_ createBaseTmpDir
|
||||||
|
$ afterAll_ deleteBaseTmpDir
|
||||||
|
$ Spec.spec
|
@ -1,4 +1,5 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE PackageImports #-}
|
||||||
|
|
||||||
|
|
||||||
module Utils where
|
module Utils where
|
||||||
@ -18,7 +19,6 @@ import Control.Monad.IfElse
|
|||||||
whenM
|
whenM
|
||||||
)
|
)
|
||||||
import qualified Data.ByteString as BS
|
import qualified Data.ByteString as BS
|
||||||
import qualified Data.ByteString.Lazy as BSL
|
|
||||||
import Data.IORef
|
import Data.IORef
|
||||||
(
|
(
|
||||||
newIORef
|
newIORef
|
||||||
@ -26,23 +26,28 @@ import Data.IORef
|
|||||||
, writeIORef
|
, writeIORef
|
||||||
, IORef
|
, IORef
|
||||||
)
|
)
|
||||||
import "hpath-directory" System.Posix.RawFilePath.Directory
|
import HPath.IO
|
||||||
|
import HPath.IO.Errors
|
||||||
import Prelude hiding (appendFile, readFile, writeFile)
|
import Prelude hiding (appendFile, readFile, writeFile)
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
(
|
(
|
||||||
fromJust
|
fromJust
|
||||||
)
|
)
|
||||||
|
import qualified HPath as P
|
||||||
import System.IO.Unsafe
|
import System.IO.Unsafe
|
||||||
(
|
(
|
||||||
unsafePerformIO
|
unsafePerformIO
|
||||||
)
|
)
|
||||||
import qualified System.Posix.RawFilePath.Directory.Traversals as DT
|
import qualified System.Posix.Directory.Traversals as DT
|
||||||
|
import System.Posix.Env.ByteString
|
||||||
|
(
|
||||||
|
getEnv
|
||||||
|
)
|
||||||
import Data.ByteString
|
import Data.ByteString
|
||||||
(
|
(
|
||||||
ByteString
|
ByteString
|
||||||
)
|
)
|
||||||
import qualified Data.ByteString.Lazy as L
|
import qualified Data.ByteString.Lazy as L
|
||||||
import System.Posix.FilePath
|
|
||||||
import System.Posix.Files.ByteString
|
import System.Posix.Files.ByteString
|
||||||
(
|
(
|
||||||
groupExecuteMode
|
groupExecuteMode
|
||||||
@ -56,14 +61,18 @@ import System.Posix.Files.ByteString
|
|||||||
, unionFileModes
|
, unionFileModes
|
||||||
)
|
)
|
||||||
|
|
||||||
baseTmpDir :: IORef (Maybe ByteString)
|
import qualified "unix" System.Posix.IO.ByteString as SPI
|
||||||
{-# NOINLINE baseTmpDir #-}
|
import qualified "unix-bytestring" System.Posix.IO.ByteString as SPB
|
||||||
baseTmpDir = unsafePerformIO (newIORef Nothing)
|
|
||||||
|
|
||||||
|
|
||||||
tmpDir :: IORef (Maybe ByteString)
|
|
||||||
|
baseTmpDir :: ByteString
|
||||||
|
baseTmpDir = "test/HPath/IO/tmp/"
|
||||||
|
|
||||||
|
|
||||||
|
tmpDir :: IORef ByteString
|
||||||
{-# NOINLINE tmpDir #-}
|
{-# NOINLINE tmpDir #-}
|
||||||
tmpDir = unsafePerformIO (newIORef Nothing)
|
tmpDir = unsafePerformIO (newIORef baseTmpDir)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -74,63 +83,75 @@ tmpDir = unsafePerformIO (newIORef Nothing)
|
|||||||
|
|
||||||
setTmpDir :: ByteString -> IO ()
|
setTmpDir :: ByteString -> IO ()
|
||||||
{-# NOINLINE setTmpDir #-}
|
{-# NOINLINE setTmpDir #-}
|
||||||
setTmpDir bs = do
|
setTmpDir bs = writeIORef tmpDir (baseTmpDir `BS.append` bs)
|
||||||
tmp <- fromJust <$> readIORef baseTmpDir
|
|
||||||
writeIORef tmpDir (Just (tmp `BS.append` bs))
|
|
||||||
|
|
||||||
|
|
||||||
createTmpDir :: IO ()
|
createTmpDir :: IO ()
|
||||||
{-# NOINLINE createTmpDir #-}
|
{-# NOINLINE createTmpDir #-}
|
||||||
createTmpDir = do
|
createTmpDir = do
|
||||||
tmp <- fromJust <$> readIORef tmpDir
|
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
|
||||||
void $ createDir newDirPerms tmp
|
tmp <- P.parseRel =<< readIORef tmpDir
|
||||||
|
void $ createDir newDirPerms (pwd P.</> tmp)
|
||||||
|
|
||||||
|
|
||||||
deleteTmpDir :: IO ()
|
deleteTmpDir :: IO ()
|
||||||
{-# NOINLINE deleteTmpDir #-}
|
{-# NOINLINE deleteTmpDir #-}
|
||||||
deleteTmpDir = do
|
deleteTmpDir = do
|
||||||
tmp <- fromJust <$> readIORef tmpDir
|
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
|
||||||
void $ deleteDir tmp
|
tmp <- P.parseRel =<< readIORef tmpDir
|
||||||
|
void $ deleteDir (pwd P.</> tmp)
|
||||||
|
|
||||||
|
|
||||||
|
createBaseTmpDir :: IO ()
|
||||||
|
{-# NOINLINE createBaseTmpDir #-}
|
||||||
|
createBaseTmpDir = do
|
||||||
|
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
|
||||||
|
tmp <- P.parseRel baseTmpDir
|
||||||
|
void $ createDir newDirPerms (pwd P.</> tmp)
|
||||||
|
|
||||||
|
|
||||||
deleteBaseTmpDir :: IO ()
|
deleteBaseTmpDir :: IO ()
|
||||||
{-# NOINLINE deleteBaseTmpDir #-}
|
{-# NOINLINE deleteBaseTmpDir #-}
|
||||||
deleteBaseTmpDir = do
|
deleteBaseTmpDir = do
|
||||||
tmp <- fromJust <$> readIORef baseTmpDir
|
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
|
||||||
contents <- getDirsFiles tmp
|
tmp <- P.parseRel baseTmpDir
|
||||||
|
contents <- getDirsFiles (pwd P.</> tmp)
|
||||||
forM_ contents deleteDir
|
forM_ contents deleteDir
|
||||||
void $ deleteDir tmp
|
void $ deleteDir (pwd P.</> tmp)
|
||||||
|
|
||||||
|
|
||||||
withRawTmpDir :: (ByteString -> IO a) -> IO a
|
withRawTmpDir :: (P.Path P.Abs -> IO a) -> IO a
|
||||||
{-# NOINLINE withRawTmpDir #-}
|
{-# NOINLINE withRawTmpDir #-}
|
||||||
withRawTmpDir f = do
|
withRawTmpDir f = do
|
||||||
tmp <- fromJust <$> readIORef tmpDir
|
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
|
||||||
f tmp
|
tmp <- P.parseRel =<< readIORef tmpDir
|
||||||
|
f (pwd P.</> tmp)
|
||||||
|
|
||||||
|
|
||||||
getRawTmpDir :: IO ByteString
|
getRawTmpDir :: IO ByteString
|
||||||
{-# NOINLINE getRawTmpDir #-}
|
{-# NOINLINE getRawTmpDir #-}
|
||||||
getRawTmpDir = withRawTmpDir (return . flip BS.append "/")
|
getRawTmpDir = withRawTmpDir (return . flip BS.append "/" . P.fromAbs)
|
||||||
|
|
||||||
|
|
||||||
withTmpDir :: ByteString -> (ByteString -> IO a) -> IO a
|
withTmpDir :: ByteString -> (P.Path P.Abs -> IO a) -> IO a
|
||||||
{-# NOINLINE withTmpDir #-}
|
{-# NOINLINE withTmpDir #-}
|
||||||
withTmpDir ip f = do
|
withTmpDir ip f = do
|
||||||
tmp <- fromJust <$> readIORef tmpDir
|
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
|
||||||
let p = tmp </> ip
|
tmp <- P.parseRel =<< readIORef tmpDir
|
||||||
|
p <- (pwd P.</> tmp P.</>) <$> P.parseRel ip
|
||||||
f p
|
f p
|
||||||
|
|
||||||
|
|
||||||
withTmpDir' :: ByteString
|
withTmpDir' :: ByteString
|
||||||
-> ByteString
|
-> ByteString
|
||||||
-> (ByteString -> ByteString -> IO a)
|
-> (P.Path P.Abs -> P.Path P.Abs -> IO a)
|
||||||
-> IO a
|
-> IO a
|
||||||
{-# NOINLINE withTmpDir' #-}
|
{-# NOINLINE withTmpDir' #-}
|
||||||
withTmpDir' ip1 ip2 f = do
|
withTmpDir' ip1 ip2 f = do
|
||||||
tmp <- fromJust <$> readIORef tmpDir
|
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
|
||||||
let p1 = tmp </> ip1
|
tmp <- P.parseRel =<< readIORef tmpDir
|
||||||
let p2 = tmp </> ip2
|
p1 <- (pwd P.</> tmp P.</>) <$> P.parseRel ip1
|
||||||
|
p2 <- (pwd P.</> tmp P.</>) <$> P.parseRel ip2
|
||||||
f p1 p2
|
f p1 p2
|
||||||
|
|
||||||
|
|
||||||
@ -163,10 +184,6 @@ createDir' :: ByteString -> IO ()
|
|||||||
{-# NOINLINE createDir' #-}
|
{-# NOINLINE createDir' #-}
|
||||||
createDir' dest = withTmpDir dest (createDir newDirPerms)
|
createDir' dest = withTmpDir dest (createDir newDirPerms)
|
||||||
|
|
||||||
createDirIfMissing' :: ByteString -> IO ()
|
|
||||||
{-# NOINLINE createDirIfMissing' #-}
|
|
||||||
createDirIfMissing' dest = withTmpDir dest (createDirIfMissing newDirPerms)
|
|
||||||
|
|
||||||
createDirRecursive' :: ByteString -> IO ()
|
createDirRecursive' :: ByteString -> IO ()
|
||||||
{-# NOINLINE createDirRecursive' #-}
|
{-# NOINLINE createDirRecursive' #-}
|
||||||
createDirRecursive' dest = withTmpDir dest (createDirRecursive newDirPerms)
|
createDirRecursive' dest = withTmpDir dest (createDirRecursive newDirPerms)
|
||||||
@ -207,7 +224,7 @@ recreateSymlink' inputFileP outputFileP cm =
|
|||||||
noWritableDirPerms :: ByteString -> IO ()
|
noWritableDirPerms :: ByteString -> IO ()
|
||||||
{-# NOINLINE noWritableDirPerms #-}
|
{-# NOINLINE noWritableDirPerms #-}
|
||||||
noWritableDirPerms path = withTmpDir path $ \p ->
|
noWritableDirPerms path = withTmpDir path $ \p ->
|
||||||
setFileMode p perms
|
setFileMode (P.fromAbs p) perms
|
||||||
where
|
where
|
||||||
perms = ownerReadMode
|
perms = ownerReadMode
|
||||||
`unionFileModes` ownerExecuteMode
|
`unionFileModes` ownerExecuteMode
|
||||||
@ -219,19 +236,19 @@ noWritableDirPerms path = withTmpDir path $ \p ->
|
|||||||
|
|
||||||
noPerms :: ByteString -> IO ()
|
noPerms :: ByteString -> IO ()
|
||||||
{-# NOINLINE noPerms #-}
|
{-# NOINLINE noPerms #-}
|
||||||
noPerms path = withTmpDir path $ \p -> setFileMode p nullFileMode
|
noPerms path = withTmpDir path $ \p -> setFileMode (P.fromAbs p) nullFileMode
|
||||||
|
|
||||||
|
|
||||||
normalDirPerms :: ByteString -> IO ()
|
normalDirPerms :: ByteString -> IO ()
|
||||||
{-# NOINLINE normalDirPerms #-}
|
{-# NOINLINE normalDirPerms #-}
|
||||||
normalDirPerms path =
|
normalDirPerms path =
|
||||||
withTmpDir path $ \p -> setFileMode p newDirPerms
|
withTmpDir path $ \p -> setFileMode (P.fromAbs p) newDirPerms
|
||||||
|
|
||||||
|
|
||||||
normalFilePerms :: ByteString -> IO ()
|
normalFilePerms :: ByteString -> IO ()
|
||||||
{-# NOINLINE normalFilePerms #-}
|
{-# NOINLINE normalFilePerms #-}
|
||||||
normalFilePerms path =
|
normalFilePerms path =
|
||||||
withTmpDir path $ \p -> setFileMode p newFilePerms
|
withTmpDir path $ \p -> setFileMode (P.fromAbs p) newFilePerms
|
||||||
|
|
||||||
|
|
||||||
getFileType' :: ByteString -> IO FileType
|
getFileType' :: ByteString -> IO FileType
|
||||||
@ -239,7 +256,7 @@ getFileType' :: ByteString -> IO FileType
|
|||||||
getFileType' path = withTmpDir path getFileType
|
getFileType' path = withTmpDir path getFileType
|
||||||
|
|
||||||
|
|
||||||
getDirsFiles' :: ByteString -> IO [ByteString]
|
getDirsFiles' :: ByteString -> IO [P.Path P.Abs]
|
||||||
{-# NOINLINE getDirsFiles' #-}
|
{-# NOINLINE getDirsFiles' #-}
|
||||||
getDirsFiles' path = withTmpDir path getDirsFiles
|
getDirsFiles' path = withTmpDir path getDirsFiles
|
||||||
|
|
||||||
@ -259,20 +276,15 @@ deleteDirRecursive' :: ByteString -> IO ()
|
|||||||
deleteDirRecursive' p = withTmpDir p deleteDirRecursive
|
deleteDirRecursive' p = withTmpDir p deleteDirRecursive
|
||||||
|
|
||||||
|
|
||||||
canonicalizePath' :: ByteString -> IO ByteString
|
canonicalizePath' :: ByteString -> IO (P.Path P.Abs)
|
||||||
{-# NOINLINE canonicalizePath' #-}
|
{-# NOINLINE canonicalizePath' #-}
|
||||||
canonicalizePath' p = withTmpDir p canonicalizePath
|
canonicalizePath' p = withTmpDir p canonicalizePath
|
||||||
|
|
||||||
|
|
||||||
writeFile' :: ByteString -> ByteString -> IO ()
|
writeFile' :: ByteString -> ByteString -> IO ()
|
||||||
{-# NOINLINE writeFile' #-}
|
{-# NOINLINE writeFile' #-}
|
||||||
writeFile' ip bs =
|
writeFile' ip bs =
|
||||||
withTmpDir ip $ \p -> writeFile p Nothing bs
|
withTmpDir ip $ \p -> writeFile p bs
|
||||||
|
|
||||||
writeFileL' :: ByteString -> BSL.ByteString -> IO ()
|
|
||||||
{-# NOINLINE writeFileL' #-}
|
|
||||||
writeFileL' ip bs =
|
|
||||||
withTmpDir ip $ \p -> writeFileL p Nothing bs
|
|
||||||
|
|
||||||
|
|
||||||
appendFile' :: ByteString -> ByteString -> IO ()
|
appendFile' :: ByteString -> ByteString -> IO ()
|
||||||
@ -284,10 +296,15 @@ appendFile' ip bs =
|
|||||||
allDirectoryContents' :: ByteString -> IO [ByteString]
|
allDirectoryContents' :: ByteString -> IO [ByteString]
|
||||||
{-# NOINLINE allDirectoryContents' #-}
|
{-# NOINLINE allDirectoryContents' #-}
|
||||||
allDirectoryContents' ip =
|
allDirectoryContents' ip =
|
||||||
withTmpDir ip $ \p -> DT.allDirectoryContents' p
|
withTmpDir ip $ \p -> DT.allDirectoryContents' (P.fromAbs p)
|
||||||
|
|
||||||
|
|
||||||
readFile' :: ByteString -> IO ByteString
|
readFile' :: ByteString -> IO ByteString
|
||||||
{-# NOINLINE readFile' #-}
|
{-# NOINLINE readFile' #-}
|
||||||
readFile' p = withTmpDir p (fmap L.toStrict . readFile)
|
readFile' p = withTmpDir p readFile
|
||||||
|
|
||||||
|
|
||||||
|
readFileEOF' :: ByteString -> IO L.ByteString
|
||||||
|
{-# NOINLINE readFileEOF' #-}
|
||||||
|
readFileEOF' p = withTmpDir p readFileEOF
|
||||||
|
|
52
update-gh-pages.sh
Executable file
52
update-gh-pages.sh
Executable file
@ -0,0 +1,52 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
SOURCE_BRANCH="master"
|
||||||
|
TARGET_BRANCH="gh-pages"
|
||||||
|
REPO="https://${GH_TOKEN}@github.com/hasufell/hpath"
|
||||||
|
DOC_LOCATION="/dist/doc/html/hpath"
|
||||||
|
|
||||||
|
|
||||||
|
# Pull requests and commits to other branches shouldn't try to deploy,
|
||||||
|
# just build to verify
|
||||||
|
if [ "$TRAVIS_PULL_REQUEST" != "false" -o "$TRAVIS_BRANCH" != "$SOURCE_BRANCH" ]; then
|
||||||
|
echo "Skipping docs deploy."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
cd "$HOME"
|
||||||
|
git config --global user.email "travis@travis-ci.org"
|
||||||
|
git config --global user.name "travis-ci"
|
||||||
|
git clone --branch=${TARGET_BRANCH} ${REPO} ${TARGET_BRANCH} || exit 1
|
||||||
|
|
||||||
|
# docs
|
||||||
|
cd ${TARGET_BRANCH} || exit 1
|
||||||
|
echo "Removing old docs."
|
||||||
|
rm -rf *
|
||||||
|
echo "Adding new docs."
|
||||||
|
cp -rf "${TRAVIS_BUILD_DIR}${DOC_LOCATION}"/* . || exit 1
|
||||||
|
|
||||||
|
# If there are no changes to the compiled out (e.g. this is a README update)
|
||||||
|
# then just bail.
|
||||||
|
if [ -z "`git diff --exit-code`" ]; then
|
||||||
|
echo "No changes to the output on this push; exiting."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
git add -- .
|
||||||
|
|
||||||
|
if [[ -e ./index.html ]] ; then
|
||||||
|
echo "Commiting docs."
|
||||||
|
git commit -m "Lastest docs updated
|
||||||
|
|
||||||
|
travis build: $TRAVIS_BUILD_NUMBER
|
||||||
|
commit: $TRAVIS_COMMIT
|
||||||
|
auto-pushed to gh-pages"
|
||||||
|
|
||||||
|
git push origin $TARGET_BRANCH
|
||||||
|
echo "Published docs to gh-pages."
|
||||||
|
else
|
||||||
|
echo "Error: docs are empty."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
Loading…
Reference in New Issue
Block a user