Split packages into hpath{,-io,-filepath}

This commit is contained in:
Julian Ospald 2020-01-04 17:52:21 +01:00
parent 4e07fcf5b2
commit 09eea518b8
No known key found for this signature in database
GPG Key ID: 511B62C09D50CD28
61 changed files with 349 additions and 229 deletions

2
.ghci
View File

@ -1,2 +0,0 @@
:set -package HUnit -package hspec
:set -package template-haskell

View File

@ -17,7 +17,7 @@ matrix:
addons: {apt: {packages: [cabal-install-3.0,ghc-8.4.4], sources: [hvr-ghc]}} addons: {apt: {packages: [cabal-install-3.0,ghc-8.4.4], sources: [hvr-ghc]}}
- env: CABALVER=3.0 GHCVER=8.6.5 - env: CABALVER=3.0 GHCVER=8.6.5
addons: {apt: {packages: [cabal-install-3.0,ghc-8.6.5], sources: [hvr-ghc]}} addons: {apt: {packages: [cabal-install-3.0,ghc-8.6.5], sources: [hvr-ghc]}}
- env: CABALVER=3.0 GHCVER=8.8.1 UPDATE_GH_PAGES=yes - env: CABALVER=3.0 GHCVER=8.8.1
addons: {apt: {packages: [cabal-install-3.0,ghc-8.8.1], sources: [hvr-ghc]}} addons: {apt: {packages: [cabal-install-3.0,ghc-8.8.1], sources: [hvr-ghc]}}
- 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]}}
@ -40,16 +40,16 @@ install:
- cabal install --installdir=$HOME/.cabal/bin doctest - cabal install --installdir=$HOME/.cabal/bin doctest
script: script:
- cabal build --enable-tests - cabal build --enable-tests all
- cabal test - cabal test all
- ./run-doctests.sh - ./hpath/run-doctests.sh
- cabal check - ./hpath-filepath/run-doctests.sh
- cabal sdist - (cd hpath && cabal check)
- cabal haddock --haddock-hyperlink-source --haddock-html-location=https://hackage.haskell.org/package/\$pkg-\$version/docs/ - (cd hpath-filepath && cabal check)
- cabal install --lib - (cd hpath-io && cabal check)
- cabal sdist all
after_script: - cabal haddock --haddock-hyperlink-source --haddock-html-location=https://hackage.haskell.org/package/\$pkg-\$version/docs/ all
- ./update-gh-pages.sh - cabal install --lib all
notifications: notifications:
email: email:

View File

@ -1,87 +1,17 @@
# HPath # HPath libraries
[![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) [![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)
Support for well-typed paths in Haskell. Also provides ByteString based filepath Set of libraries to deal with filepaths and files.
manipulation.
## Motivation ## Motivation
The motivation came during development of * filepaths should be type-safe (absolute, relative, ...)
[hsfm](https://github.com/hasufell/hsfm) * filepaths should be ByteString under the hood, see [Abstract FilePath Proposal (AFPP)](https://gitlab.haskell.org/ghc/ghc/wikis/proposal/abstract-file-path)
which has a pretty strict File type, but lacks a strict Path type, e.g. * file high-level operations should be platform-specific, exception-stable, safe and as atomic as possible
for user input.
The library that came closest to my needs was ## Projects
[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)
```
* [hpath](./hpath): Support for well-typed paths
* [hpath-filepath](./hpath-filepath): ByteString based filepath manipulation (can be used without hpath)
* [hpath-io](./hpath-io): high-level file API (recursive copy, writeFile etc.) using hpath

4
cabal.project Normal file
View File

@ -0,0 +1,4 @@
packages: ./hpath
./hpath-filepath
./hpath-io

View File

@ -0,0 +1,5 @@
# Revision history for hpath-filepath
## 0.9.3 -- 2020-01-04
* First version. Released on an unsuspecting world.

30
hpath-filepath/LICENSE Normal file
View File

@ -0,0 +1,30 @@
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.

23
hpath-filepath/README.md Normal file
View File

@ -0,0 +1,23 @@
# 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'.
## 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

View File

@ -0,0 +1,39 @@
name: hpath-filepath
version: 0.9.3
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

View File

@ -18,5 +18,4 @@ fi
set -x set -x
cabal exec doctest -- -isrc -XOverloadedStrings System.Posix.FilePath cabal exec doctest -- -ihpath-filepath/src -XOverloadedStrings System.Posix.FilePath
cabal exec doctest -- -isrc -XOverloadedStrings HPath

5
hpath-io/CHANGELOG.md Normal file
View File

@ -0,0 +1,5 @@
# Revision history for hpath-io
## 0.9.3 -- YYYY-mm-dd
* First version. Released on an unsuspecting world.

30
hpath-io/LICENSE Normal file
View File

@ -0,0 +1,30 @@
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.

26
hpath-io/README.md Normal file
View File

@ -0,0 +1,26 @@
# 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.
## 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.
## 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

2
hpath-io/Setup.hs Normal file
View File

@ -0,0 +1,2 @@
import Distribution.Simple
main = defaultMain

View File

@ -1,7 +1,8 @@
name: hpath name: hpath-io
version: 0.9.2 version: 0.9.3
synopsis: Support for well-typed paths synopsis: High-level IO operations on files/directories
description: Support for well-typed paths, utilizing ByteString under the hood. description: High-level IO operations on files/directories, utilizing type-safe Paths
-- bug-reports:
license: BSD3 license: BSD3
license-file: LICENSE license-file: LICENSE
author: Julian Ospald <hasufell@posteo.de> author: Julian Ospald <hasufell@posteo.de>
@ -17,40 +18,33 @@ tested-with: GHC==7.10.3
, GHC==8.6.5 , GHC==8.6.5
, GHC==8.8.1 , GHC==8.8.1
extra-source-files: README.md extra-source-files: README.md
CHANGELOG CHANGELOG.md
cbits/dirutils.h cbits/dirutils.h
library library
if os(windows) if os(windows)
build-depends: unbuildable<0 build-depends: unbuildable<0
buildable: False buildable: False
hs-source-dirs: src/ exposed-modules: HPath.IO,
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, HPath.IO.Errors,
System.Posix.Directory.Foreign, System.Posix.Directory.Foreign,
System.Posix.Directory.Traversals, System.Posix.Directory.Traversals,
System.Posix.FD, System.Posix.FD
System.Posix.FilePath c-sources: cbits/dirutils.c
other-modules: HPath.Internal
-- other-modules:
-- other-extensions:
build-depends: base >= 4.8 && <5 build-depends: base >= 4.8 && <5
, IfElse , IfElse
, bytestring >= 0.10.0.0 , bytestring >= 0.10.0.0
, deepseq , hpath
, exceptions , hpath-filepath
, hspec
, streamly >= 0.7 , streamly >= 0.7
, unix >= 2.5 , unix >= 2.5
, unix-bytestring , unix-bytestring
, utf8-string , utf8-string
, word8 hs-source-dirs: src
default-language: Haskell2010
test-suite spec test-suite spec
if os(windows) if os(windows)
@ -94,6 +88,7 @@ test-suite spec
, IfElse , IfElse
, bytestring >= 0.10.0.0 , bytestring >= 0.10.0.0
, hpath , hpath
, hpath-io
, hspec >= 1.3 , hspec >= 1.3
, process , process
, unix , unix
@ -103,4 +98,3 @@ test-suite spec
source-repository head source-repository head
type: git type: git
location: https://github.com/hasufell/hpath location: https://github.com/hasufell/hpath

View File

@ -149,9 +149,9 @@ toConstr RecursiveFailure {} = "RecursiveFailure"
isSameFile, isDestinationInSource, isRecursiveFailure :: HPathIOException -> Bool isSameFile, isDestinationInSource, isRecursiveFailure :: HPathIOException -> Bool
isSameFile ex = toConstr (ex :: HPathIOException) == toConstr SameFile{} isSameFile ex = toConstr (ex :: HPathIOException) == toConstr (SameFile mempty mempty)
isDestinationInSource ex = toConstr (ex :: HPathIOException) == toConstr DestinationInSource{} isDestinationInSource ex = toConstr (ex :: HPathIOException) == (toConstr $ DestinationInSource mempty mempty)
isRecursiveFailure ex = toConstr (ex :: HPathIOException) == toConstr RecursiveFailure{} isRecursiveFailure ex = toConstr (ex :: HPathIOException) == (toConstr $ RecursiveFailure mempty)
isReadContentsFailed, isCreateDirFailed, isCopyFileFailed, isRecreateSymlinkFailed ::RecursiveFailureHint -> Bool isReadContentsFailed, isCreateDirFailed, isCopyFileFailed, isRecreateSymlinkFailed ::RecursiveFailureHint -> Bool

View File

@ -13,7 +13,6 @@ import GHC.IO.Exception
( (
IOErrorType(..) IOErrorType(..)
) )
import System.Process
import Utils import Utils

View File

@ -127,7 +127,8 @@ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
CollectFailures CollectFailures
(system $ "diff -r --no-dereference " (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"

View File

@ -106,7 +106,8 @@ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
FailEarly FailEarly
(system $ "diff -r --no-dereference " (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"
@ -114,7 +115,8 @@ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
tmpDir' <- getRawTmpDir tmpDir' <- getRawTmpDir
(system $ "diff -r --no-dereference " (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"
@ -122,7 +124,8 @@ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
FailEarly FailEarly
(system $ "diff -r --no-dereference " (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"

View File

@ -91,7 +91,8 @@ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
FailEarly FailEarly
(system $ "diff -r --no-dereference " (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"

View File

@ -13,7 +13,6 @@ import GHC.IO.Exception
( (
IOErrorType(..) IOErrorType(..)
) )
import System.Process
import Utils import Utils

View File

@ -13,7 +13,6 @@ import GHC.IO.Exception
( (
IOErrorType(..) IOErrorType(..)
) )
import System.Process
import Utils import Utils

View File

@ -13,7 +13,6 @@ import GHC.IO.Exception
( (
IOErrorType(..) IOErrorType(..)
) )
import System.Process
import Utils import Utils

View File

@ -1,19 +1,24 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
import qualified Data.ByteString as BS
import Data.IORef
import Test.Hspec import Test.Hspec
import Test.Hspec.Runner import Test.Hspec.Runner
import Test.Hspec.Formatters import Test.Hspec.Formatters
import qualified Spec import qualified Spec
import Utils import Utils
import System.Posix.Temp.ByteString (mkdtemp)
-- TODO: chardev, blockdev, namedpipe, socket -- TODO: chardev, blockdev, namedpipe, socket
main :: IO () main :: IO ()
main = main = do
tmpBase <- mkdtemp "/tmp/"
writeIORef baseTmpDir (Just (tmpBase `BS.append` "/"))
putStrLn $ ("Temporary test directory at: " <> show tmpBase)
hspecWith hspecWith
defaultConfig { configFormatter = Just progress } defaultConfig { configFormatter = Just progress }
$ beforeAll_ createBaseTmpDir
$ afterAll_ deleteBaseTmpDir $ afterAll_ deleteBaseTmpDir
$ Spec.spec $ Spec.spec

View File

@ -39,10 +39,6 @@ import System.IO.Unsafe
unsafePerformIO unsafePerformIO
) )
import qualified System.Posix.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
@ -61,18 +57,14 @@ import System.Posix.Files.ByteString
, unionFileModes , unionFileModes
) )
import qualified "unix" System.Posix.IO.ByteString as SPI baseTmpDir :: IORef (Maybe ByteString)
import qualified "unix-bytestring" System.Posix.IO.ByteString as SPB {-# NOINLINE baseTmpDir #-}
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 baseTmpDir) tmpDir = unsafePerformIO (newIORef Nothing)
@ -83,49 +75,39 @@ tmpDir = unsafePerformIO (newIORef baseTmpDir)
setTmpDir :: ByteString -> IO () setTmpDir :: ByteString -> IO ()
{-# NOINLINE setTmpDir #-} {-# NOINLINE setTmpDir #-}
setTmpDir bs = writeIORef tmpDir (baseTmpDir `BS.append` bs) setTmpDir bs = do
tmp <- fromJust <$> readIORef baseTmpDir
writeIORef tmpDir (Just (tmp `BS.append` bs))
createTmpDir :: IO () createTmpDir :: IO ()
{-# NOINLINE createTmpDir #-} {-# NOINLINE createTmpDir #-}
createTmpDir = do createTmpDir = do
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs tmp <- P.parseAbs =<< (fromJust <$> readIORef tmpDir)
tmp <- P.parseRel =<< readIORef tmpDir void $ createDir newDirPerms tmp
void $ createDir newDirPerms (pwd P.</> tmp)
deleteTmpDir :: IO () deleteTmpDir :: IO ()
{-# NOINLINE deleteTmpDir #-} {-# NOINLINE deleteTmpDir #-}
deleteTmpDir = do deleteTmpDir = do
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs tmp <- P.parseAbs =<< (fromJust <$> readIORef tmpDir)
tmp <- P.parseRel =<< readIORef tmpDir void $ deleteDir tmp
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
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs tmp <- (fromJust <$> readIORef baseTmpDir) >>= P.parseAbs
tmp <- P.parseRel baseTmpDir contents <- getDirsFiles tmp
contents <- getDirsFiles (pwd P.</> tmp)
forM_ contents deleteDir forM_ contents deleteDir
void $ deleteDir (pwd P.</> tmp) void $ deleteDir tmp
withRawTmpDir :: (P.Path P.Abs -> IO a) -> IO a withRawTmpDir :: (P.Path P.Abs -> IO a) -> IO a
{-# NOINLINE withRawTmpDir #-} {-# NOINLINE withRawTmpDir #-}
withRawTmpDir f = do withRawTmpDir f = do
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs tmp <- P.parseAbs =<< (fromJust <$> readIORef tmpDir)
tmp <- P.parseRel =<< readIORef tmpDir f tmp
f (pwd P.</> tmp)
getRawTmpDir :: IO ByteString getRawTmpDir :: IO ByteString
@ -136,9 +118,8 @@ getRawTmpDir = withRawTmpDir (return . flip BS.append "/" . P.fromAbs)
withTmpDir :: ByteString -> (P.Path P.Abs -> 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
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs tmp <- P.parseAbs =<< (fromJust <$> readIORef tmpDir)
tmp <- P.parseRel =<< readIORef tmpDir p <- (tmp P.</>) <$> P.parseRel ip
p <- (pwd P.</> tmp P.</>) <$> P.parseRel ip
f p f p
@ -148,10 +129,9 @@ withTmpDir' :: ByteString
-> IO a -> IO a
{-# NOINLINE withTmpDir' #-} {-# NOINLINE withTmpDir' #-}
withTmpDir' ip1 ip2 f = do withTmpDir' ip1 ip2 f = do
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs tmp <- P.parseAbs =<< (fromJust <$> readIORef tmpDir)
tmp <- P.parseRel =<< readIORef tmpDir p1 <- (tmp P.</>) <$> P.parseRel ip1
p1 <- (pwd P.</> tmp P.</>) <$> P.parseRel ip1 p2 <- (tmp P.</>) <$> P.parseRel ip2
p2 <- (pwd P.</> tmp P.</>) <$> P.parseRel ip2
f p1 p2 f p1 p2

37
hpath/README.md Normal file
View File

@ -0,0 +1,37 @@
# 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.
## 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...
* 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

2
hpath/Setup.hs Normal file
View File

@ -0,0 +1,2 @@
import Distribution.Simple
main = defaultMain

44
hpath/hpath.cabal Normal file
View File

@ -0,0 +1,44 @@
name: hpath
version: 0.9.2
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
HPath.Internal
build-depends: base >= 4.8 && <5
, bytestring >= 0.10.0.0
, deepseq
, exceptions
, hpath-filepath
, word8
source-repository head
type: git
location: https://github.com/hasufell/hpath

21
hpath/run-doctests.sh Executable file
View File

@ -0,0 +1,21 @@
#!/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
cabal exec doctest -- -ihpath/src -XOverloadedStrings HPath

View File

@ -1,55 +0,0 @@
#!/bin/bash
SOURCE_BRANCH="master"
TARGET_BRANCH="gh-pages"
REPO="https://${GITHUB_TOKEN}@github.com/hasufell/hpath"
if [ -z "${UPDATE_GH_PAGES}" ] ; then
exit 0
fi
# 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}"/dist-newstyle/build/x86_64-linux/ghc-*/hpath-*/doc/html/hpath/* . || 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