From 7bb206831b37dde7a0f3208f445c8e645cc36a18 Mon Sep 17 00:00:00 2001 From: Alexander Foremny Date: Tue, 13 Feb 2024 06:59:18 +0100 Subject: add `serve` command --- README.md | 59 ++++++++++++++++++++++++++++++++--------------------------- app/Main.hs | 44 +++++++++++++++++++++++++------------------- astore.cabal | 8 ++++++-- 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 -- cgit v1.2.3