packages feed

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

{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}

module KitchenSink.Engine.NewArticle where

import Data.Aeson (encode, object, (.=))
import Data.Aeson.Types (Pair)
import Data.Char (isAlphaNum)
import Data.List (filter)
import Data.Maybe (fromMaybe)
import Data.Text qualified as Text
import Data.Text.IO qualified as Text
import Data.Text.Lazy qualified as TextLazy
import Data.Text.Lazy.Encoding qualified as TextLazy
import Data.Time.Clock (UTCTime, getCurrentTime)
import System.Directory (doesDirectoryExist, doesFileExist)
import System.Exit (exitFailure)
import System.FilePath.Posix ((</>))
import System.IO (hPutStrLn, stderr)

import KitchenSink.Prelude

data Args
    = Args
    { srcDir :: FilePath
    , title :: Text
    , layout :: Maybe Text
    , author :: Maybe Text
    }

run :: Args -> IO ()
run cmd = do
    srcDirExists <- doesDirectoryExist cmd.srcDir
    if not srcDirExists
        then do
            hPutStrLn stderr $ cmd.srcDir <> " does not exist"
            exitFailure
        else do
            let filename = cmd.srcDir </> Text.unpack slug <> ".cmark"
            fileExists <- doesFileExist filename
            if fileExists
                then do
                    hPutStrLn stderr $ filename <> " already exists"
                    exitFailure
                else do
                    now <- getCurrentTime
                    Text.writeFile filename (articleContents layoutName authorName cmd.title now)
                    putStrLn $ "Created new article in " <> filename
  where
    slug = slugify cmd.title
    layoutName = maybe "article" Text.toLower cmd.layout
    authorName = fromMaybe "Anonymous Author" cmd.author

slugify :: Text -> Text
slugify =
    Text.intercalate "-"
        . filter (not . Text.null)
        . Text.split (== '-')
        . Text.map toDash
        . Text.toLower
  where
    toDash c
        | isAlphaNum c = c
        | otherwise = '-'

jsonLine :: [Pair] -> Text
jsonLine = TextLazy.toStrict . TextLazy.decodeUtf8 . encode . object

articleContents :: Text -> Text -> Text -> UTCTime -> Text
articleContents layoutName authorName title now =
    Text.unlines
        [ "=base:build-info.json"
        , jsonLine ["layout" .= layoutName, "publicationStatus" .= ("Public" :: Text)]
        , ""
        , "=base:preamble.json"
        , jsonLine ["author" .= authorName, "title" .= title, "date" .= now]
        , ""
        , "=base:topic.json"
        , jsonLine ["topics" .= ([] :: [Text]), "keywords" .= ([] :: [Text])]
        , ""
        , "=base:social.json"
        , jsonLine []
        , ""
        , "=base:main-css.css"
        , ""
        , "=base:summary.cmark"
        , ""
        , "TODO: write a short summary of \"" <> title <> "\"."
        , ""
        , "=base:main-content.cmark"
        , ""
        , "# " <> title
        , ""
        , "TODO: write the article content."
        ]