@@ -24,9 +24,13 @@ module Swarm.Util (
24
24
(?) ,
25
25
maxOn ,
26
26
maximum0 ,
27
- readFileMay ,
28
27
cycleEnum ,
29
28
29
+ -- * Directory utilities
30
+ readFileMay ,
31
+ readFileMayT ,
32
+ getSwarmHistoryPath ,
33
+
30
34
-- * English language utilities
31
35
quote ,
32
36
squote ,
@@ -46,20 +50,27 @@ module Swarm.Util (
46
50
liftText ,
47
51
) where
48
52
49
- import Control.Monad (unless )
53
+ import Control.Monad (unless , when )
50
54
import Control.Monad.Error.Class
51
55
import Data.Either.Validation
52
56
import Data.Int (Int64 )
53
57
import Data.Maybe (fromMaybe )
54
58
import Data.Text (Text )
55
59
import qualified Data.Text as T
60
+ import qualified Data.Text.IO as T
56
61
import Data.Yaml
57
62
import Language.Haskell.TH
58
63
import Language.Haskell.TH.Syntax (lift )
59
64
import Linear (V2 )
60
65
import qualified NLP.Minimorph.English as MM
61
66
import NLP.Minimorph.Util ((<+>) )
62
- import System.Directory (doesFileExist )
67
+ import System.Directory (
68
+ XdgDirectory (XdgData ),
69
+ createDirectoryIfMissing ,
70
+ getXdgDirectory ,
71
+ )
72
+ import System.FilePath
73
+ import System.IO.Error (catchIOError )
63
74
64
75
infixr 1 ?
65
76
@@ -83,27 +94,38 @@ maximum0 :: (Num a, Ord a) => [a] -> a
83
94
maximum0 [] = 0
84
95
maximum0 xs = maximum xs
85
96
86
- -- | Safely attempt to read a file, returning @Nothing@ if the file
87
- -- does not exist. \"Safely\" should be read in scare quotes here,
88
- -- since /e.g./ we do nothing to guard against the possibility of a
89
- -- race condition where the file is deleted after the existence
90
- -- check but before trying to read it. But it's not like we're
91
- -- worried about security or anything here.
92
- readFileMay :: FilePath -> IO (Maybe String )
93
- readFileMay file = do
94
- b <- doesFileExist file
95
- case b of
96
- False -> return Nothing
97
- True -> Just <$> readFile file
98
-
99
97
-- | Take the successor of an 'Enum' type, wrapping around when it
100
98
-- reaches the end.
101
99
cycleEnum :: (Eq e , Enum e , Bounded e ) => e -> e
102
100
cycleEnum e
103
101
| e == maxBound = minBound
104
102
| otherwise = succ e
105
103
106
- --------------------------------------------------
104
+ ------------------------------------------------------------
105
+ -- Directory stuff
106
+
107
+ -- | Safely attempt to read a file.
108
+ readFileMay :: FilePath -> IO (Maybe String )
109
+ readFileMay = catchIO . readFile
110
+
111
+ -- | Safely attempt to (efficiently) read a file.
112
+ readFileMayT :: FilePath -> IO (Maybe Text )
113
+ readFileMayT = catchIO . T. readFile
114
+
115
+ -- | Turns any IO error into Nothing.
116
+ catchIO :: IO a -> IO (Maybe a )
117
+ catchIO act = (Just <$> act) `catchIOError` (\ _ -> return Nothing )
118
+
119
+ -- | Get path to swarm history, optionally creating necessary
120
+ -- directories. This could fail if user has bad permissions
121
+ -- on his own $HOME or $XDG_DATA_HOME which is unlikely.
122
+ getSwarmHistoryPath :: Bool -> IO FilePath
123
+ getSwarmHistoryPath createDirs = do
124
+ swarmData <- getXdgDirectory XdgData " swarm"
125
+ when createDirs (createDirectoryIfMissing True swarmData)
126
+ pure (swarmData </> " history" )
127
+
128
+ ------------------------------------------------------------
107
129
-- Some language-y stuff
108
130
109
131
-- | Prepend a noun with the proper indefinite article (\"a\" or \"an\").
0 commit comments