2 Module : Gargantext.Prelude.GargDB
3 Description : Useful Tools near Prelude of the project
4 Copyright : (c) CNRS, 2017-Present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
10 TODO_1: qualitative tests (human)
11 TODO_2: quantitative tests (coded)
15 {-# LANGUAGE QuasiQuotes #-}
17 module Gargantext.Database.GargDB
20 import Control.Exception
21 import Control.Lens (view)
22 import Control.Monad.Reader (MonadReader)
23 import Data.Aeson (ToJSON, toJSON)
24 import Data.Text (Text)
25 import Data.Tuple.Extra (both)
26 import GHC.IO (FilePath)
27 import Gargantext.Database.Prelude (HasConfig(..))
28 import Gargantext.Prelude
29 import Gargantext.Prelude.Config
30 import Gargantext.Prelude.Crypto.Hash
31 import System.Directory (createDirectoryIfMissing)
32 import System.IO.Error
33 import System.Random (newStdGen)
34 import qualified Data.Text as Text
35 import qualified System.Directory as SD
37 -------------------------------------------------------------------
38 -- | Main Class to use (just declare needed functions)
41 read :: FilePath -> IO a
43 rm :: (a, FilePath) -> IO ()
44 mv :: (a, FilePath) -> FilePath -> IO ()
47 -- | Why not this class too ?
48 class ToJSON parameters => GargDB' parameters gargdata where
49 write' :: parameters -> gargdata -> IO ()
50 read' :: parameters -> IO gargdata
52 rm' :: gargdata -> parameters -> IO ()
53 mv' :: gargdata -> parameters -> parameters -> IO ()
55 -------------------------------------------------------------------
56 -- | Deprecated Class, use GargDB instead
57 class SaveFile a where
58 saveFile' :: FilePath -> a -> IO ()
60 class ReadFile a where
61 readFile' :: FilePath -> IO a
63 -------------------------------------------------------------------
64 -------------------------------------------------------------------
65 type GargFilePath = (FolderPath, FileName)
67 type FolderPath = FilePath
68 type FileName = FilePath
70 --------------------------------
72 dataFilePath :: (ToJSON a) => a -> GargFilePath
73 dataFilePath = toPath . hash . show . toJSON
75 randomFilePath :: ( MonadReader env m
80 (foldPath, fileName) <- liftBase
85 pure (foldPath, fileName)
88 -- | toPath' : how to hash text to path
90 >>> toPath' (1,2) ("","helloword")
93 >>> toPath' (2,2) ("","helloword")
96 >>> toPath' (2,3) ("","helloword")
99 toPath :: Text -> (FolderPath, FileName)
100 toPath tx = both Text.unpack $ toPath' (2,3) ("", tx)
102 toPath' :: (Int,Int) -> (Text,Text) -> (Text,Text)
103 toPath' (n,m) (t,x) = foldl' (\tx _ -> toPath'' m tx) (t,x) [1..n]
105 toPath'' :: Int -> (Text, Text) -> (Text, Text)
106 toPath'' n (fp,fn) = (fp'',fn')
108 (fp',fn') = Text.splitAt n fn
109 fp'' = Text.intercalate "/" [fp,fp']
111 -------------------------------------------------------------------
112 type DataPath = FilePath
113 toFilePath :: FilePath -> FilePath -> FilePath
114 toFilePath fp1 fp2 = fp1 <> "/" <> fp2
116 -------------------------------------------------------------------
119 -- | For example, this write file with a random filepath
120 -- better use a hash of json of Type used to parameter as input
122 writeFile :: ( MonadReader env m
129 dataPath <- view $ hasConfig . gc_datafilepath
131 (foldPath, fileName) <- randomFilePath
133 let filePath = toFilePath foldPath fileName
134 dataFoldPath = toFilePath dataPath foldPath
135 dataFileName = toFilePath dataPath filePath
137 _ <- liftBase $ createDirectoryIfMissing True dataFoldPath
138 _ <- liftBase $ saveFile' dataFileName a
144 -- | Example to read a file with Type
145 readGargFile :: ( MonadReader env m
152 dataPath <- view $ hasConfig . gc_datafilepath
153 liftBase $ readFile' $ toFilePath dataPath fp
157 rmFile :: ( MonadReader env m
162 rmFile = onDisk_1 SD.removeFile
164 cpFile :: (MonadReader env m, MonadBase IO m, HasConfig env)
165 => FilePath -> FilePath -> m ()
166 cpFile = onDisk_2 SD.copyFile
170 mvFile :: ( MonadReader env m
174 => FilePath -> FilePath -> m ()
180 ------------------------------------------------------------------------
181 onDisk_1 :: ( MonadReader env m
185 => (FilePath -> IO ()) -> FilePath -> m ()
186 onDisk_1 action fp = do
187 dataPath <- view $ hasConfig . gc_datafilepath
188 liftBase $ action (toFilePath dataPath fp) `catch` handleExists
191 | isDoesNotExistError e = return ()
192 | otherwise = throwIO e
195 onDisk_2 :: ( MonadReader env m
199 => (FilePath -> FilePath -> IO ())
203 onDisk_2 action fp1 fp2 = do
204 dataPath <- view $ hasConfig . gc_datafilepath
205 let fp1' = toFilePath dataPath fp1
206 fp2' = toFilePath dataPath fp2
207 liftBase $ action fp1' fp2' `catch` handleExists
210 | isDoesNotExistError e = return ()
211 | otherwise = throwIO e
212 ------------------------------------------------------------------------