aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Alexander Foremny <aforemny@posteo.de>2024-02-13 06:59:18 +0100
committerLibravatar Alexander Foremny <aforemny@posteo.de>2024-02-13 06:59:52 +0100
commit7bb206831b37dde7a0f3208f445c8e645cc36a18 (patch)
treefd9cd99615c12255a25330c012e190f36f6e9664
parent0c86cb5623df1053d302175505ad834a623ed0a4 (diff)
add `serve` command
-rw-r--r--README.md59
-rw-r--r--app/Main.hs44
-rw-r--r--astore.cabal8
3 files changed, 63 insertions, 48 deletions
diff --git a/README.md b/README.md
index c9eba83..07ca11d 100644
--- a/README.md
+++ b/README.md
@@ -1,33 +1,38 @@
-```console
-❯ tree data/
-data/
-├── c
-│   ├── 1.json
-│   └── 2.json
-└── j
- ├── 1.json
- └── 2.json
-```
+# astore
-```console
-> SELECT . FROM c
-{"id":2,"j_id":3}
-{"foo":"bar","id":1,"j_id":1}
+astore is a simple library and CLI program to query and manipulate a file hierarchy of JSON files.
-> SELECT id FROM c
-{"c.id":2}
-{"c.id":1}
+## Commands
-> SELECT c.id, j.id, is_j FROM c LEFT JOIN j ON j.id == c.j_id
-{"c.id":2,"is_j":null,"j.id":null}
-{"c.id":1,"is_j":true,"j.id":1}
+### `astore repl`
-> SELECT c.id, j.id FROM c RIGHT JOIN j ON j.id == c.j_id
-{"c.id":null,"j.id":2}
-{"c.id":1,"j.id":1}
+```console
+$ cabal run astore -- repl
+>>> SELECT * FROM c
+{"foo":"bar","id":1,"j_id":1}
+{"id":2,"j_id":3}
+>>> SELECT * FROM j
+{"id":1,"is_j":true}
+{"id":2,"is_j":true}
+>>> SELECT c.id, j.id, is_j FROM c LEFT JOIN j ON j.id == c.j_id
+{"c":{"id":1},"is_j":true,"j":{"id":1}}
+{"c":{"id":2},"is_j":null,"j":{"id":null}}
+>>> SELECT c.id, j.id FROM c RIGHT JOIN j ON j.id == c.j_id
+{"c":{"id":1},"j":{"id":1}}
+{"c":{"id":null},"j":{"id":2}}
+>>> SELECT c.id, j.id FROM c FULL JOIN j ON j.id == c.j_id
+{"c":{"id":1},"j":{"id":1}}
+{"c":{"id":2},"j":{"id":null}}
+{"c":{"id":null},"j":{"id":2}}
+>>> SELECT c, j FROM c EMBED j ON j.id == c.j_id
+{"c":null,"j":[{"id":1,"is_j":true}]}
+{"c":null,"j":[]}
+```
-> SELECT c.id, j.id FROM c FULL JOIN j ON j.id == c.j_id
-{"c.id":2,"j.id":null}
-{"c.id":1,"j.id":1}
-{"c.id":null,"j.id":2}
+### `astore serve`
+
+```console
+$ cabal run astore -- serve &
+$ curl -d 'SELECT * FROM c' http://localhost:8080
+[{"foo":"bar","id":1,"j_id":1},{"id":2,"j_id":3}]
```
diff --git a/app/Main.hs b/app/Main.hs
index 5bb783e..8622317 100644
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -7,7 +7,12 @@ import Control.Applicative ((<**>))
import Control.Monad.Trans (liftIO)
import Data.Aeson qualified as J
import Data.ByteString.Lazy.Char8 qualified as LB
+import Data.ByteString.Lazy.UTF8 qualified as LB
import Data.String (IsString (fromString))
+import Network.HTTP.Types.Method qualified as W
+import Network.HTTP.Types.Status qualified as W
+import Network.Wai qualified as W
+import Network.Wai.Handler.Warp qualified as W
import Options.Applicative qualified as A
import Store qualified as Q
import System.Console.Repline qualified as R
@@ -24,39 +29,28 @@ args =
data Cmd
= Repl
+ | Serve
cmd_ :: A.Parser Cmd
cmd_ =
A.hsubparser . mconcat $
[ A.command "repl" . A.info replCmd $
- A.progDesc "Interactively execute statements"
+ A.progDesc "Interactively execute statements",
+ A.command "serve" . A.info serveCmd $
+ A.progDesc "Run webserver"
]
-replCmd :: A.Parser Cmd
+replCmd, serveCmd :: A.Parser Cmd
replCmd =
pure Repl
+serveCmd =
+ pure Serve
main :: IO ()
main = do
+ setCurrentDirectory "./data"
A.execParser (A.info (args <**> A.helper) A.idm) >>= \case
Args {cmd = Repl} -> do
- setCurrentDirectory "./data"
-
- {-
- mapM_
- ( \q -> do
- printf "> %s\n" (show q)
- query' q
- )
- [ "SELECT * FROM c",
- "SELECT * FROM j",
- "SELECT c.id, j.id, is_j FROM c LEFT JOIN j ON j.id == c.j_id",
- "SELECT c.id, j.id FROM c RIGHT JOIN j ON j.id == c.j_id",
- "SELECT c.id, j.id FROM c FULL JOIN j ON j.id == c.j_id",
- "SELECT c.id, j FROM c EMBED j ON j.id == c.j_id"
- ]
- -}
-
-- TODO Catch `ParseError` exception
--
-- @topic repl
@@ -73,6 +67,18 @@ main = do
(R.Word (\_ -> pure ["SELECT"]))
(pure ())
(pure R.Exit)
+ Args {cmd = Serve} -> do
+ W.runEnv 8080 $ \req respond -> do
+ if
+ | W.requestMethod req == W.methodPost -> do
+ q <-
+ fromString @Q.Query . LB.toString
+ <$> W.lazyRequestBody req
+ r <- Q.query q
+ respond . W.responseLBS W.status200 [] $
+ J.encode r
+ | otherwise ->
+ respond $ W.responseLBS W.status200 [] "OK"
query' :: Q.Query -> IO ()
query' q = mapM_ (LB.putStrLn . J.encode) =<< Q.query q
diff --git a/astore.cabal b/astore.cabal
index 568bb41..232b01f 100644
--- a/astore.cabal
+++ b/astore.cabal
@@ -56,7 +56,7 @@ executable astore
hs-source-dirs: app
default-language: GHC2021
default-extensions:
- LambdaCase NoFieldSelectors OverloadedRecordDot OverloadedStrings
+ LambdaCase MultiWayIf NoFieldSelectors OverloadedRecordDot OverloadedStrings
build-depends:
aeson,
@@ -64,6 +64,10 @@ executable astore
base,
bytestring,
directory,
+ http-types,
mtl,
optparse-applicative,
- repline
+ repline,
+ utf8-string,
+ wai,
+ warp