Skip to content

Commit c904264

Browse files
authored
Use System.FilePath Library (#50)
1 parent fac264b commit c904264

File tree

9 files changed

+66
-150
lines changed

9 files changed

+66
-150
lines changed

haskellings.cabal

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ library
3939
, containers
4040
, directory
4141
, extra
42+
, filepath
4243
, fsnotify
4344
, tasty
4445
, tasty-hunit
@@ -71,6 +72,7 @@ test-suite haskellings-tests
7172
base >=4.7 && <5
7273
, containers
7374
, directory
75+
, filepath
7476
, haskellings
7577
, hspec
7678
, HUnit
@@ -87,6 +89,7 @@ test-suite unit-tests
8789
base >=4.7 && <5
8890
, containers
8991
, directory
92+
, filepath
9093
, haskellings
9194
, tasty
9295
, tasty-hunit

haskellings.nix

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{ mkDerivation, aeson, ansi-terminal, base, containers, directory
2-
, extra, fsnotify, hspec, HUnit, lib, process, tasty, tasty-hunit
2+
, extra, filepath, fsnotify, hspec, HUnit, lib, process, tasty, tasty-hunit
33
, time, yaml
44
}:
55
mkDerivation {
@@ -10,12 +10,12 @@ mkDerivation {
1010
isExecutable = true;
1111
doCheck = false;
1212
libraryHaskellDepends = [
13-
aeson ansi-terminal base containers directory extra fsnotify
13+
aeson ansi-terminal base containers directory extra filepath fsnotify
1414
process tasty tasty-hunit time yaml
1515
];
1616
executableHaskellDepends = [ base containers extra ];
1717
testHaskellDepends = [
18-
base containers directory hspec HUnit tasty tasty-hunit time
18+
base containers directory filepath hspec HUnit tasty tasty-hunit time
1919
];
2020
homepage = "https://github.com/MondayMorningHaskell/haskellings#readme";
2121
license = lib.licenses.bsd3;

src/Config.hs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import Data.Yaml (decodeFileEither)
1414
import System.Console.ANSI
1515
import System.Directory
1616
import System.Environment (lookupEnv)
17+
import System.FilePath (takeDirectory, takeFileName, (</>))
1718
import System.IO
1819

1920
import DirectoryUtils
@@ -44,7 +45,7 @@ haskellingsVersion :: String
4445
haskellingsVersion = "0.8.0.0"
4546

4647
mainProjectExercisesDir :: String
47-
mainProjectExercisesDir = makeRelative "exercises"
48+
mainProjectExercisesDir = "exercises"
4849

4950
-- A listing of packages required by exercises, so we can use them
5051
-- to filter Stack snapshots
@@ -151,12 +152,12 @@ loadBaseConfigPaths :: IO (Either ConfigError (FilePath, FilePath, FilePath))
151152
loadBaseConfigPaths = do
152153
projectRoot' <- findProjectRoot
153154
case projectRoot' of
154-
Nothing -> return (Left NoProjectRootError)
155+
Nothing -> return (Left NoProjectRootError)
155156
Just projectRoot -> loadBaseConfigPathsWithProjectRoot projectRoot
156157

157158
loadBaseConfigPathsWithProjectRoot :: FilePath -> IO (Either ConfigError (FilePath, FilePath, FilePath))
158159
loadBaseConfigPathsWithProjectRoot projectRoot = do
159-
let configPath = projectRoot `pathJoin` configFileName
160+
let configPath = projectRoot </> configFileName
160161
configExists <- doesFileExist configPath
161162
baseConfig <- if configExists
162163
then do
@@ -185,17 +186,17 @@ findGhc = do
185186
Just ghcSearchDir -> do
186187
nextDirs <- listDirectory ghcSearchDir
187188
results <- forM nextDirs $ \subPath -> do
188-
let fullPath = ghcSearchDir `pathJoin` subPath
189+
let fullPath = ghcSearchDir </> subPath
189190
subContents <- safeListDirectory fullPath
190-
return $ fmap (pathJoin fullPath) (find ghcPred subContents)
191+
return $ fmap (fullPath </>) (find ghcPred subContents)
191192
case catMaybes results of
192193
[] -> return Nothing
193-
(fp : _) -> return $ Just (fp `pathJoin` "bin" `pathJoin` "ghc")
194+
(fp : _) -> return $ Just (fp </> "bin" </> "ghc")
194195

195196
-- Determine a directory is a valid "ghc" directory.
196197
-- It must start with "ghc" and end with our version number.
197198
ghcPred :: FilePath -> Bool
198-
ghcPred path = isPrefixOf "ghc" (basename path) && isSuffixOf ghcVersionNumber path
199+
ghcPred path = isPrefixOf "ghc" (takeFileName path) && isSuffixOf ghcVersionNumber path
199200

200201
findStackPackageDb :: IO (Maybe FilePath)
201202
findStackPackageDb = do
@@ -212,7 +213,7 @@ findStackPackageDb = do
212213
-- The GHC version path might look like {hash}/8.10.4/lib/x86_64-linux-ghc-8.10.4
213214
-- We want to get the package path, at {hash}/8.10.4/pkgdb
214215
pkgPathFromGhcPath :: FilePath -> FilePath
215-
pkgPathFromGhcPath ghcVersionDir = pathJoin (dropDirectoryLevel (dropDirectoryLevel ghcVersionDir)) "pkgdb"
216+
pkgPathFromGhcPath ghcVersionDir = takeDirectory (takeDirectory ghcVersionDir) </> "pkgdb"
216217

217218
snapshotPackagePredicate :: FilePath -> IO Bool
218219
snapshotPackagePredicate fp = if not (ghcVersion `isSuffixOf` fp)
@@ -241,15 +242,15 @@ findStackSnapshotsDir = if isWindows
241242
findStackSnapshotsDirUnix :: IO (Maybe FilePath)
242243
findStackSnapshotsDirUnix = do
243244
homeDir <- getHomeDirectory
244-
let dir = homeDir `pathJoin` ".stack" `pathJoin` "snapshots"
245+
let dir = homeDir </> ".stack" </> "snapshots"
245246
returnIfDirExists dir
246247

247248
findStackSnapshotsDirWindows :: IO (Maybe FilePath)
248249
findStackSnapshotsDirWindows = do
249250
dir' <- lookupEnv "STACK_ROOT"
250251
case dir' of
251252
Nothing -> return Nothing
252-
Just dir -> returnIfDirExists (dir `pathJoin` "snapshots")
253+
Just dir -> returnIfDirExists (dir </> "snapshots")
253254

254255
findGhcSearchDir :: IO (Maybe FilePath)
255256
findGhcSearchDir = if isWindows
@@ -261,9 +262,9 @@ findGhcSearchDirUnix = do
261262
isCi <- envIsCi
262263
homeDir <- if isCi
263264
-- Unintuitively, "/home" is not the same as "~" on Circle CI
264-
then return ("/home" `pathJoin` "stackage")
265+
then return ("/home" </> "stackage")
265266
else getHomeDirectory
266-
let dir = homeDir `pathJoin` ".stack" `pathJoin` "programs"
267+
let dir = homeDir </> ".stack" </> "programs"
267268
returnIfDirExists dir
268269

269270
findGhcSearchDirWindows :: IO (Maybe FilePath)
@@ -272,5 +273,5 @@ findGhcSearchDirWindows = do
272273
case localAppDataDir' of
273274
Nothing -> return Nothing
274275
Just localAppDataDir -> do
275-
let dir = localAppDataDir `pathJoin` "Programs" `pathJoin` "stack"
276+
let dir = localAppDataDir </> "Programs" </> "stack"
276277
returnIfDirExists dir

src/DirectoryUtils.hs

Lines changed: 3 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -5,63 +5,12 @@ import Data.List (dropWhileEnd, isInfixOf, isSuffixOf)
55
import Data.List.Extra (takeWhileEnd)
66
import qualified Data.Sequence as S
77
import System.Directory
8+
import System.FilePath (takeDirectory, takeFileName, (</>))
89
import System.Info (os)
910

1011
isWindows :: Bool
1112
isWindows = os `notElem` ["linux", "unix", "darwin"]
1213

13-
basename :: FilePath -> FilePath
14-
basename fp = takeWhileEnd (\c -> c /= '/' && c /= '\\') trimmedFp
15-
where
16-
trimmedFp = if isRelativeEnd fp then init fp else fp
17-
18-
-- Like doing "cd .." with this filepath
19-
dropDirectoryLevel :: FilePath -> FilePath
20-
dropDirectoryLevel fp = init $ dropWhileEnd (\c -> c /= '/' && c /= '\\') trimmedFp
21-
where
22-
trimmedFp = if isRelativeEnd fp then init fp else fp
23-
24-
makeRelative :: FilePath -> FilePath
25-
makeRelative path = if isWindows
26-
then makeRelativeWindows path
27-
else makeRelativeUnix path
28-
29-
makeRelativeWindows :: FilePath -> FilePath
30-
makeRelativeWindows = makeRelative' '\\'
31-
32-
makeRelativeUnix :: FilePath -> FilePath
33-
makeRelativeUnix = makeRelative' '/'
34-
35-
makeRelative' :: Char -> FilePath -> FilePath
36-
makeRelative' delimiter fp = if head fp == delimiter
37-
then fp
38-
else delimiter : fp
39-
40-
isRelativeEnd :: FilePath -> Bool
41-
isRelativeEnd "" = False
42-
isRelativeEnd fp = last fp == '/' || last fp == '\\'
43-
44-
isRelativeBegin :: FilePath -> Bool
45-
isRelativeBegin "" = False
46-
isRelativeBegin fp = head fp == '/' || head fp == '\\'
47-
48-
pathJoin :: FilePath -> FilePath -> FilePath
49-
pathJoin fp1 fp2 = if isWindows
50-
then pathJoinWindows fp1 fp2
51-
else pathJoinUnix fp1 fp2
52-
53-
pathJoinUnix :: FilePath -> FilePath -> FilePath
54-
pathJoinUnix = pathJoin' '/'
55-
56-
pathJoinWindows :: FilePath -> FilePath -> FilePath
57-
pathJoinWindows = pathJoin' '\\'
58-
59-
pathJoin' :: Char -> FilePath -> FilePath -> FilePath
60-
pathJoin' delimiter fp1 fp2 = case (isRelativeEnd fp1, isRelativeBegin fp2) of
61-
(True, True) -> fp1 ++ tail fp2
62-
(False, False) -> fp1 ++ makeRelative' delimiter fp2
63-
_ -> fp1 ++ fp2
64-
6514
returnIfDirExists :: FilePath -> IO (Maybe FilePath)
6615
returnIfDirExists fp = do
6716
exists <- doesDirectoryExist fp
@@ -74,7 +23,7 @@ searchForDirectoryContaining searchRoot directoryToFind = fpBFS predicate (S.sin
7423
where
7524
predicate fp = do
7625
isDirectory <- doesDirectoryExist fp
77-
return $ isDirectory && makeRelative directoryToFind `isSuffixOf` fp
26+
return $ isDirectory && directoryToFind == takeFileName fp
7827

7928
-- Given a "file predicate" function
8029
fpBFS :: (FilePath -> IO Bool) -> S.Seq FilePath -> IO (Maybe FilePath)
@@ -89,7 +38,7 @@ fpBFS predicate searchQueue = case S.viewl searchQueue of
8938
if currentIsDir
9039
then do
9140
contents <- safeListDirectory currentRoot
92-
let results = map (pathJoin currentRoot) contents
41+
let results = map (currentRoot </>) contents
9342
fpBFS predicate $ rest S.>< S.fromList results
9443
else fpBFS predicate rest
9544

src/RunCommands.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Data.List (maximumBy)
66
import qualified Data.Map as M
77
import Data.Yaml (encodeFile)
88
import System.Directory
9+
import System.FilePath ((</>))
910
import System.Process
1011

1112
import Config
@@ -78,7 +79,7 @@ runConfigureWithProjectRoot projectRoot = do
7879
ghc <- getLine
7980
putStrLn "Please enter Stack package DB path (or leave blank): "
8081
stackPath <- getLine
81-
let configPath = projectRoot `pathJoin` configFileName
82+
let configPath = projectRoot </> configFileName
8283
alreadyExists <- doesFileExist configPath
8384
when alreadyExists $ removeFile configPath
8485
let config = BaseConfig

src/Utils.hs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Data.List.Extra
77
import Data.Maybe (fromJust, isJust)
88
import System.Directory
99
import System.Exit
10+
import System.FilePath (takeBaseName, (</>))
1011
import System.IO
1112
import System.Process
1213

@@ -19,7 +20,7 @@ isHaskellFile = isSuffixOf ".hs"
1920

2021
-- Probably a good idea to first check that it is a Haskell file first
2122
haskellModuleName :: FilePath -> FilePath
22-
haskellModuleName fp = dropEnd 3 (basename fp)
23+
haskellModuleName = takeBaseName
2324

2425
haskellFileName :: FilePath -> FilePath
2526
haskellFileName exName = exName ++ ".hs"
@@ -55,9 +56,9 @@ createExerciseProcess config (ExerciseInfo exerciseName exDirectory exType _) =
5556
exIsRunnable = exType /= CompileOnly
5657
exFilename = haskellFileName exerciseName
5758
root = projectRoot config
58-
fullSourcePath = root `pathJoin` exercisesExt config `pathJoin` exDirectory `pathJoin` exFilename
59-
genDirPath = root `pathJoin` "/generated_files/" `pathJoin` exDirectory
60-
genExecutablePath = genDirPath `pathJoin` haskellModuleName exFilename
59+
fullSourcePath = root </> exercisesExt config </> exDirectory </> exFilename
60+
genDirPath = root </> "generated_files" </> exDirectory
61+
genExecutablePath = genDirPath </> haskellModuleName exFilename
6162
baseArgs = [fullSourcePath, "-odir", genDirPath, "-hidir", genDirPath]
6263
execArgs = if exIsRunnable then baseArgs ++ ["-o", genExecutablePath] else baseArgs
6364
finalArgs = execArgs ++ ["-package-db", packageDb config]
@@ -158,4 +159,4 @@ fileContainsNotDone fullFp = do
158159
isDoneLine l = (upper . filter (not . isSpace) $ l) == "--IAMNOTDONE"
159160

160161
fullExerciseFp :: FilePath -> FilePath -> ExerciseInfo -> FilePath
161-
fullExerciseFp projectRoot exercisesExt (ExerciseInfo exName exDir _ _) = projectRoot `pathJoin` exercisesExt `pathJoin` exDir `pathJoin` haskellFileName exName
162+
fullExerciseFp projectRoot exercisesExt (ExerciseInfo exName exDir _ _) = projectRoot </> exercisesExt </> exDir </> haskellFileName exName

src/Watcher.hs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Control.Concurrent
44
import Control.Monad (forever, unless, void, when)
55
import qualified Data.Map as M
66
import System.Exit
7+
import System.FilePath (takeFileName, (</>))
78
import System.FSNotify
89
import System.IO (hIsEOF)
910

@@ -16,8 +17,8 @@ watchExercises :: ProgramConfig -> IO ()
1617
watchExercises config = runExerciseWatch config allExercises
1718

1819
shouldCheckFile :: ExerciseInfo -> Event -> Bool
19-
shouldCheckFile (ExerciseInfo exName _ _ _) (Added fp _ _) = basename fp == haskellFileName exName
20-
shouldCheckFile (ExerciseInfo exName _ _ _) (Modified fp _ _) = basename fp == haskellFileName exName
20+
shouldCheckFile (ExerciseInfo exName _ _ _) (Added fp _ _) = takeFileName fp == haskellFileName exName
21+
shouldCheckFile (ExerciseInfo exName _ _ _) (Modified fp _ _) = takeFileName fp == haskellFileName exName
2122
shouldCheckFile _ _ = False
2223

2324
-- This event should be a modification of one of our exercise files
@@ -50,7 +51,7 @@ runExerciseWatch config (firstEx : restExs) = do
5051
let conf = defaultConfig { confDebounce = Debounce 1 }
5152
withManagerConf conf $ \mgr -> do
5253
signalMVar <- newEmptyMVar
53-
stopAction <- watchTree mgr (projectRoot config `pathJoin` exercisesExt config) (shouldCheckFile firstEx)
54+
stopAction <- watchTree mgr (projectRoot config </> exercisesExt config) (shouldCheckFile firstEx)
5455
(processEvent config firstEx signalMVar)
5556
userInputThread <- forkIO $ forever (watchForUserInput config firstEx)
5657
takeMVar signalMVar

tests/Main.hs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import qualified Data.Map as M
44
import Data.Time
55
import System.Directory
66
import System.Exit
7+
import System.FilePath ((</>))
78
import System.IO
89
import Test.Hspec
910
import Test.HUnit
@@ -21,7 +22,7 @@ main = do
2122
case loadResult of
2223
Left _ -> error "Unable to find project root or GHC 8.8.4!"
2324
Right paths@(root, _, _) -> do
24-
createDirectoryIfMissing True (root `pathJoin` "tests" `pathJoin` "test_gen")
25+
createDirectoryIfMissing True (root </> "tests" </> "test_gen")
2526
hspec $ describe "Basic Compile Tests" $ do
2627
compileTests1 paths
2728
compileTests2 paths
@@ -38,9 +39,9 @@ main = do
3839

3940
compileBeforeHook :: (FilePath, FilePath, FilePath) -> ExerciseInfo -> FilePath -> IO (String, RunResult)
4041
compileBeforeHook (projectRoot, ghcPath, packageDb) exInfo outFile = do
41-
let fullFp = projectRoot `pathJoin` "tests" `pathJoin` "test_gen" `pathJoin` outFile
42+
let fullFp = projectRoot </> "tests" </> "test_gen" </> outFile
4243
outHandle <- openFile fullFp WriteMode
43-
let conf = ProgramConfig projectRoot ghcPath packageDb "/tests/exercises/" stdin outHandle stderr M.empty
44+
let conf = ProgramConfig projectRoot ghcPath packageDb ("tests" </> "exercises") stdin outHandle stderr M.empty
4445
resultExit <- compileAndRunExercise conf exInfo
4546
hClose outHandle
4647
programOutput <- readFile fullFp
@@ -237,8 +238,8 @@ beforeWatchHook (projectRoot, ghcPath, stackPackageDb) outFile = do
237238
copyFile (addFullDirectory "Types1Orig.hs") fullDest1
238239
copyFile (addFullDirectory "Types2Orig.hs") fullDest2
239240
-- Build Configuration
240-
let fullFp = projectRoot `pathJoin` "tests" `pathJoin` "test_gen" `pathJoin` outFile
241-
let fullIn = projectRoot `pathJoin` "tests" `pathJoin` "watcher_tests.in"
241+
let fullFp = projectRoot </> "tests" </> "test_gen" </> outFile
242+
let fullIn = projectRoot </> "tests" </> "watcher_tests.in"
242243
outHandle <- openFile fullFp WriteMode
243244
inHandle <- openFile fullIn ReadMode
244245
lock1 <- newEmptyMVar
@@ -255,9 +256,9 @@ beforeWatchHook (projectRoot, ghcPath, stackPackageDb) outFile = do
255256
removeFile fullDest2
256257
readFile fullFp
257258
where
258-
testExercisesDir = makeRelative ("tests" `pathJoin` "exercises")
259-
watcherTypesDir = "tests" `pathJoin` "exercises" `pathJoin` "watcher_types"
260-
addFullDirectory = pathJoin (projectRoot `pathJoin` watcherTypesDir)
259+
testExercisesDir = "tests" </> "exercises"
260+
watcherTypesDir = "tests" </> "exercises" </> "watcher_types"
261+
addFullDirectory = (</>) (projectRoot </> watcherTypesDir)
261262
fullDest1 = addFullDirectory "Types1.hs"
262263
fullDest2 = addFullDirectory "Types2.hs"
263264
modifications =
@@ -281,9 +282,9 @@ listTestExercises =
281282

282283
listBeforeHook :: (FilePath, FilePath, FilePath) -> FilePath -> IO String
283284
listBeforeHook (projectRoot, ghcPath, stackPackageDb) outFile = do
284-
let fullFp = projectRoot `pathJoin` "tests" `pathJoin` "test_gen" `pathJoin` outFile
285+
let fullFp = projectRoot </> "tests" </> "test_gen" </> outFile
285286
outHandle <- openFile fullFp WriteMode
286-
let conf = ProgramConfig projectRoot ghcPath stackPackageDb ("tests" `pathJoin` "exercises") stdin outHandle stderr M.empty
287+
let conf = ProgramConfig projectRoot ghcPath stackPackageDb ("tests" </> "exercises") stdin outHandle stderr M.empty
287288
listExercises' listTestExercises conf
288289
hClose outHandle
289290
readFile fullFp

0 commit comments

Comments
 (0)