add CLI draft
This commit is contained in:
5
cli/CHANGELOG.md
Normal file
5
cli/CHANGELOG.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Revision history for cli
|
||||
|
||||
## 0.1.0.0 -- YYYY-mm-dd
|
||||
|
||||
* First version. Released on an unsuspecting world.
|
||||
26
cli/LICENSE
Normal file
26
cli/LICENSE
Normal file
@@ -0,0 +1,26 @@
|
||||
Copyright (c) 2024, Alexander Foremny
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
33
cli/app/API/Collection.hs
Normal file
33
cli/app/API/Collection.hs
Normal file
@@ -0,0 +1,33 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
{-# LANGUAGE ViewPatterns #-}
|
||||
|
||||
module API.Collection where
|
||||
|
||||
import Data.Aeson qualified as A
|
||||
import Data.Aeson.KeyMap qualified as AM
|
||||
import Data.Text qualified as T
|
||||
import Process.Shell (Quotable (..), sh)
|
||||
import Debug.Trace
|
||||
|
||||
insert :: T.Text -> T.Text -> A.Object -> IO T.Text
|
||||
insert
|
||||
collectionName
|
||||
fileName
|
||||
( traceShowId -> AM.insert "$fileName" (A.String fileName) -> traceShowId ->
|
||||
A.Object -> traceShowId -> contents
|
||||
) =
|
||||
{- TODO REST/ CRUD API
|
||||
[sh|
|
||||
curl -fsS http://localhost:8081/collections/#{collectionName}/#{filePath} \
|
||||
--data #{contents}
|
||||
\|]-}
|
||||
[sh|
|
||||
set -efux
|
||||
curl -fsS http://localhost:8081 \
|
||||
--data "INSERT "'#{contents}'" INTO #{collectionName}"
|
||||
|]
|
||||
|
||||
-- TODO sh
|
||||
instance Quotable A.Value where
|
||||
toString = toString . A.encode
|
||||
88
cli/app/Main.hs
Normal file
88
cli/app/Main.hs
Normal file
@@ -0,0 +1,88 @@
|
||||
{-# LANGUAGE BlockArguments #-}
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
{-# LANGUAGE ViewPatterns #-}
|
||||
{-# LANGUAGE NoFieldSelectors #-}
|
||||
|
||||
module Main where
|
||||
|
||||
import API.Collection qualified
|
||||
import Control.Applicative ((<**>))
|
||||
import Data.Aeson qualified as J
|
||||
import Data.ByteString.Lazy qualified as LB
|
||||
import Data.Text qualified as T
|
||||
import Options.Applicative qualified as O
|
||||
import Text.ParserCombinators.ReadP qualified as R
|
||||
import Text.ParserCombinators.ReadPrec qualified as R
|
||||
import Text.Read (Read (..))
|
||||
|
||||
data Args = Args
|
||||
{ cmd :: Cmd
|
||||
}
|
||||
|
||||
args :: O.Parser Args
|
||||
args = Args <$> cmd_
|
||||
|
||||
data Cmd = Collection CollectionCmd
|
||||
|
||||
cmd_ :: O.Parser Cmd
|
||||
cmd_ =
|
||||
O.hsubparser . mconcat $
|
||||
[ O.command "collection" . O.info collectionCmd $
|
||||
O.progDesc "Manage content collections"
|
||||
]
|
||||
|
||||
data CollectionCmd = CollectionInsert
|
||||
{ filePath :: CollectionPath
|
||||
}
|
||||
|
||||
data CollectionPath = CollectionPath
|
||||
{ collectionName :: T.Text,
|
||||
fileName :: T.Text
|
||||
}
|
||||
|
||||
instance Read CollectionPath where
|
||||
readPrec = R.lift do
|
||||
(T.pack -> collectionName) <- R.munch (/= '/')
|
||||
_ <- R.string "/"
|
||||
(T.pack -> fileName) <- do
|
||||
fileName <- R.munch (liftA2 (&&) (/= '.') (/= '/'))
|
||||
fileExt <- R.string ".json"
|
||||
pure (fileName <> fileExt)
|
||||
pure CollectionPath {..}
|
||||
|
||||
instance Show CollectionPath where
|
||||
show (CollectionPath {collectionName, fileName}) =
|
||||
show (collectionName <> "/" <> fileName)
|
||||
|
||||
collectionCmd :: O.Parser Cmd
|
||||
collectionCmd =
|
||||
fmap Collection . O.hsubparser . mconcat $
|
||||
[ O.command "insert" . O.info collectionInsertCmd $
|
||||
O.progDesc "Insert an entity"
|
||||
]
|
||||
|
||||
collectionInsertCmd :: O.Parser CollectionCmd
|
||||
collectionInsertCmd =
|
||||
CollectionInsert
|
||||
<$> collectionPathArg
|
||||
|
||||
collectionPathArg :: O.Parser CollectionPath
|
||||
collectionPathArg =
|
||||
O.argument O.auto (O.metavar "COLLECTION_PATH")
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
O.execParser (O.info (args <**> O.helper) O.idm) >>= \case
|
||||
Args
|
||||
{ cmd =
|
||||
Collection
|
||||
CollectionInsert
|
||||
{ filePath = CollectionPath {collectionName, fileName}
|
||||
}
|
||||
} ->
|
||||
print
|
||||
=<< API.Collection.insert collectionName fileName
|
||||
=<< J.throwDecode
|
||||
=<< LB.getContents
|
||||
24
cli/cli.cabal
Normal file
24
cli/cli.cabal
Normal file
@@ -0,0 +1,24 @@
|
||||
cabal-version: 3.4
|
||||
name: cli
|
||||
version: 0.1.0.0
|
||||
license: BSD-2-Clause
|
||||
license-file: LICENSE
|
||||
maintainer: aforemny@posteo.de
|
||||
author: Alexander Foremny
|
||||
build-type: Simple
|
||||
extra-doc-files: CHANGELOG.md
|
||||
|
||||
executable cli
|
||||
main-is: Main.hs
|
||||
hs-source-dirs: app
|
||||
other-modules: API.Collection
|
||||
default-language: GHC2021
|
||||
ghc-options: -Wall
|
||||
build-depends:
|
||||
aeson,
|
||||
base,
|
||||
bytestring,
|
||||
filepath,
|
||||
optparse-applicative,
|
||||
sh,
|
||||
text
|
||||
22
docs/get-started-cli.md
Normal file
22
docs/get-started-cli.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Get started with the CLI
|
||||
|
||||
## Create a new project
|
||||
|
||||
Create a new folder for your project, `my-project`. Change into it, and set `AMCS_CONTENT` to the folder that should store your content.
|
||||
|
||||
```
|
||||
mkdir -p my-project
|
||||
cd my-project
|
||||
export ACMS_CONTENT=$PWD/content
|
||||
```
|
||||
|
||||
## Create a restaurant collection
|
||||
|
||||
```
|
||||
acms collection insert restaurant/1.json <<'EOF'
|
||||
{
|
||||
"name": "Biscotte Restaurant",
|
||||
"description": "Welcome to Biscotte restaurant! Restaurant Biscotte offers a cuisine based on fresh, quality products, often local, organic when possible, and always produced by passionate producers."
|
||||
}
|
||||
EOF
|
||||
```
|
||||
4
docs/get-started.md
Normal file
4
docs/get-started.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Get started
|
||||
|
||||
- [Get started with the CLI](./get-started-cli.md)
|
||||
- [Get started with the web interface](./get-started-web-interface.md)
|
||||
Reference in New Issue
Block a user