packages feed

kitchen-sink-0.1.0.0: src/KitchenSink/Engine.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}

module KitchenSink.Engine where

import Options.Generic

import Data.Text as Text
import KitchenSink.Engine.Auth (hashPassword)
import KitchenSink.Engine.Init qualified as Init
import KitchenSink.Engine.MultiSite qualified as MultiSite
import KitchenSink.Engine.NewArticle qualified as NewArticle
import KitchenSink.Engine.Produce qualified as Produce
import KitchenSink.Engine.Serve qualified as Serve
import KitchenSink.Prelude

data Action
    = Init
        { dir :: FilePath <?> "directory to create the new site in"
        , force :: Bool <?> "overwrite if directory already exists and is non-empty"
        }
    | Produce
        { srcDir :: FilePath <?> "source directory"
        , outDir :: Maybe FilePath <?> "output directory"
        , ksFile :: Maybe FilePath <?> "kitchen-sink.json file"
        , var :: [Text] <?> "variables in --var varname=value format"
        , abortOnError :: Bool <?> "stop at the first target that fails (default: report it, produce the rest, exit non-zero)"
        }
    | Serve
        { srcDir :: FilePath <?> "source directory"
        , outDir :: Maybe FilePath <?> "output directory"
        , ksFile :: Maybe FilePath <?> "kitchen-sink.json file"
        , servMode :: Serve.ServMode <?> "SERVE|DEV"
        , var :: [Text] <?> "variables in --var varname=value format"
        , httpPort :: Maybe Int <?> "port-num"
        , httpsPort :: Maybe Int <?> "port-num"
        , tlsKeyFile :: Maybe FilePath <?> "tls-private-key"
        , tlsCertFile :: Maybe FilePath <?> "tls-certificate"
        , dynamic :: Bool <?> "opt-in to request-time dynamic pages (SQLPage-style routes/sql-datasets), see kitchen-sink.json's datasources"
        }
    | NewArticle
        { srcDir :: FilePath <?> "source directory"
        , title :: Text <?> "article title"
        , layout :: Maybe Text <?> "layout name (default: article)"
        , author :: Maybe Text <?> "author name"
        }
    | MultiSite
        { configFile :: FilePath <?> "multisite config file (JSON)"
        , var :: [Text] <?> "variables in --var varname=value format"
        , httpPort :: Maybe Int <?> "port-num"
        , httpsPort :: Maybe Int <?> "port-num"
        , tlsKeyFile :: Maybe FilePath <?> "tls-private-key"
        , tlsCertFile :: Maybe FilePath <?> "tls-certificate"
        , proxyingTimeout :: Maybe Int <?> "proxy-timeout-microsecs"
        }
    | HashPassword
        { password :: Text <?> "plaintext password to hash for kitchen-sink.json's auth.provider.users[].passwordHash (see KitchenSink.Engine.Auth)"
        }
    deriving (Generic, Show)

instance ParseRecord Action

defaultMain :: IO ()
defaultMain = do
    cmd <- getRecord "kitchen-sink"
    case cmd of
        Init a b ->
            let (a', b') = coerce (a, b)
             in Init.run (Init.Args a' b')
        Produce a b c vars d ->
            let (a', b', c', d') = coerce (a, b, c, d)
             in Produce.run (Produce.Args a' b' c' (parseVars (coerce vars)) d')
        Serve a b c d vars e f g h i ->
            let (a', b', c', d', e', f', g', h', i') = coerce (a, b, c, d, e, f, g, h, i)
             in Serve.run (Serve.Args a' b' c' d' (parseVars (coerce vars)) e' f' g' h' i')
        NewArticle a b c d ->
            let (a', b', c', d') = coerce (a, b, c, d)
             in NewArticle.run (NewArticle.Args a' b' c' d')
        MultiSite a vars b c d e f ->
            let (a', b', c', d', e', f') = coerce (a, b, c, d, e, f)
             in MultiSite.run (MultiSite.Args a' (parseVars (coerce vars)) b' c' d' e' f')
        HashPassword a -> do
            hashed <- hashPassword (coerce a)
            putStrLn (Text.unpack hashed)
  where
    parseVars :: [Text] -> [(Text, Text)]
    parseVars xs = fmap parseVar xs

    parseVar :: Text -> (Text, Text)
    parseVar txt =
        let (k, v) = Text.breakOn "=" txt
         in (k, Text.drop 1 v)