-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMain.hs
More file actions
121 lines (99 loc) · 3.92 KB
/
Copy pathMain.hs
File metadata and controls
121 lines (99 loc) · 3.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
{-# HLINT ignore "Use <&>" #-}
module Main (main) where
import Control.Monad.IO.Class (MonadIO (liftIO))
import qualified Data.ByteString.Lazy.Char8 as BL
import qualified Data.Aeson as A
import System.Directory (listDirectory, doesFileExist)
import Control.Monad.Free (Free(Free, Pure))
import System.FilePath ((</>), takeExtension, dropExtension)
import JsonManipulator ()
import Data.Time (UTCTime, getCurrentTime)
import Data.List qualified as L
import Lib1 qualified
import Lib3 qualified
import System.Console.Repline (
CompleterStyle (Word),
ExitDecision (Exit),
HaskelineT,
WordCompleter,
evalRepl,
)
import System.Console.Terminal.Size (Window, size, width)
import InMemoryTables
import DataFrame (DataFrame)
import SQLParser (ErrorMessage)
type Repl a = HaskelineT IO a
final :: Repl ExitDecision
final = do
liftIO $ putStrLn "Goodbye!"
return Exit
ini :: Repl ()
ini = liftIO $ putStrLn "Welcome to select-manipulate database! Press [TAB] for auto completion."
completer :: (Monad m) => WordCompleter m
completer n = do
let names = [
"select", "*", "from", "show", "table",
"tables", "insert", "into", "values",
"set", "update", "delete"
]
return $ Prelude.filter (L.isPrefixOf n) names
-- Evaluation : handle each line user inputs
cmd :: String -> Repl ()
cmd c = do
s <- terminalWidth <$> liftIO size
result <- liftIO $ cmd' s
case result of
Left err -> liftIO $ putStrLn $ "Error: " ++ err
Right table -> liftIO $ putStrLn table
where
terminalWidth :: (Integral n) => Maybe (Window n) -> n
terminalWidth = maybe 80 width
cmd' :: Integer -> IO (Either String String)
cmd' s = do
df <- runExecuteIO $ Lib3.executeSql c
return $ Lib1.renderDataFrameAsTable s <$> df
main :: IO ()
main =
evalRepl (const $ pure ">>> ") cmd [] Nothing Nothing (Word completer) ini final
databaseDir :: String
databaseDir = "db"
databaseFormat :: String
databaseFormat = ".json"
constructFilePath :: String -> FilePath
constructFilePath tableName = databaseDir </> tableName ++ databaseFormat
runExecuteIO :: Lib3.Execution r -> IO r
runExecuteIO (Pure r) = return r
runExecuteIO (Free step) = do
next <- runStep step
runExecuteIO next
where
-- probably you will want to extend the interpreter
runStep :: Lib3.ExecutionAlgebra a -> IO a
runStep (Lib3.GetTime next) = getCurrentTime >>= return . next
runStep (Lib3.ListTables next) = do
filepaths <- listDirectory databaseDir
return $ next $ map dropExtension $ filter (\fp -> takeExtension fp == ".json") filepaths
runStep (Lib3.LoadDataFrame tableName next) = do
eitherDataFrame <- loadDataFrame tableName
return $ next eitherDataFrame
runStep (Lib3.LoadDataFrames tableNames next) = do
eitherDataFrames <- traverse loadDataFrame tableNames
return $ next $ sequence eitherDataFrames
runStep (Lib3.SaveTable table next) = do
let tableName = getTableName table
let jsonTable = A.encode table
BL.writeFile (constructFilePath tableName) jsonTable
return $ next $ Right ()
runStep (Lib3.DeleteTable _ next) =
return $ next $ Just "Delete operation not supported in Lib3"
loadDataFrame :: TableName -> IO (Either ErrorMessage DataFrame)
loadDataFrame tableName = do
let filepath = constructFilePath tableName
fileExists <- doesFileExist filepath
if fileExists then do
contents <- readFile filepath
case A.decode $ BL.pack contents :: Maybe Table of
Nothing -> return $ Left $ "Failed to deserialise table \'" ++ tableName ++ "\'"
Just table -> return $ Right (getDataFrame table)
else
return $ Left $ "Table \'" ++ tableName ++ "\' not found"