2019-01-27 21:41:21 +01:00
|
|
|
{-# LANGUAGE NamedFieldPuns #-}
|
2019-02-03 22:56:21 +01:00
|
|
|
{-# LANGUAGE FlexibleContexts #-}
|
2020-05-08 15:51:25 +02:00
|
|
|
{-# LANGUAGE ConstraintKinds #-}
|
2019-01-27 21:41:21 +01:00
|
|
|
module Blog (
|
2019-02-02 23:23:05 +01:00
|
|
|
Blog(..)
|
2019-02-15 15:11:31 +01:00
|
|
|
, Path(..)
|
2020-05-08 15:51:25 +02:00
|
|
|
, Renderer
|
2019-02-07 17:51:06 +01:00
|
|
|
, Skin(..)
|
2019-12-21 12:50:38 +01:00
|
|
|
, URL(..)
|
2019-08-27 13:23:17 +02:00
|
|
|
, Wording
|
2019-02-03 22:56:21 +01:00
|
|
|
, build
|
2020-05-08 15:51:25 +02:00
|
|
|
, template
|
2019-01-27 21:41:21 +01:00
|
|
|
) where
|
|
|
|
|
2019-02-15 15:11:31 +01:00
|
|
|
import Arguments (Arguments)
|
2019-03-02 23:40:34 +01:00
|
|
|
import qualified Arguments (name, sourceDir)
|
2019-02-02 23:23:05 +01:00
|
|
|
import Article (Article)
|
2019-02-15 18:07:59 +01:00
|
|
|
import qualified Article (at, getKey)
|
2019-02-15 15:11:31 +01:00
|
|
|
import Blog.Path (Path(..))
|
|
|
|
import qualified Blog.Path as Path (build)
|
2020-05-08 15:51:25 +02:00
|
|
|
import Blog.Template (Environment, Templates, render)
|
|
|
|
import qualified Blog.Template as Template (build)
|
2019-02-07 17:51:06 +01:00
|
|
|
import Blog.Skin (Skin(..))
|
|
|
|
import qualified Blog.Skin as Skin (build)
|
2019-12-21 12:50:38 +01:00
|
|
|
import Blog.URL (URL(..))
|
|
|
|
import qualified Blog.URL as URL (build)
|
2019-08-27 13:23:17 +02:00
|
|
|
import Blog.Wording (Wording)
|
2019-02-17 19:52:28 +01:00
|
|
|
import qualified Blog.Wording as Wording (build)
|
2019-04-28 17:27:55 +02:00
|
|
|
import Control.Monad ((>=>), filterM, foldM, forM)
|
2020-05-08 15:51:25 +02:00
|
|
|
import Control.Monad.IO.Class (MonadIO)
|
|
|
|
import Control.Monad.Reader (MonadReader, asks)
|
2019-04-28 17:27:55 +02:00
|
|
|
import Data.Map (Map, insert, lookup)
|
|
|
|
import qualified Data.Map as Map (empty, fromList)
|
2019-01-27 21:41:21 +01:00
|
|
|
import Data.Set (Set)
|
|
|
|
import qualified Data.Set as Set (empty, null, singleton, union)
|
2020-05-08 15:51:25 +02:00
|
|
|
import Data.Text (Text)
|
2019-03-02 23:40:34 +01:00
|
|
|
import Files (File(..), absolute)
|
2019-02-06 17:16:52 +01:00
|
|
|
import qualified Files (find)
|
2019-02-18 15:16:34 +01:00
|
|
|
import Prelude hiding (lookup)
|
2019-02-06 17:16:52 +01:00
|
|
|
import System.Directory (doesFileExist, withCurrentDirectory)
|
2019-02-15 15:11:31 +01:00
|
|
|
import System.FilePath ((</>), dropTrailingPathSeparator, takeExtension, takeFileName)
|
2019-04-28 17:27:55 +02:00
|
|
|
import Text.Parsec (ParseError)
|
2019-01-27 21:41:21 +01:00
|
|
|
|
2019-02-15 14:16:21 +01:00
|
|
|
type Collection = Map String Article
|
2019-01-27 21:41:21 +01:00
|
|
|
|
|
|
|
data Blog = Blog {
|
|
|
|
articles :: Collection
|
2020-05-08 15:51:25 +02:00
|
|
|
, hasRSS :: Bool
|
2019-01-27 21:41:21 +01:00
|
|
|
, name :: String
|
2019-02-15 15:11:31 +01:00
|
|
|
, path :: Path
|
2019-02-07 17:51:06 +01:00
|
|
|
, skin :: Skin
|
2019-02-15 14:16:21 +01:00
|
|
|
, tags :: Map String (Set String)
|
2020-05-08 15:51:25 +02:00
|
|
|
, templates :: Templates
|
2019-12-21 12:50:38 +01:00
|
|
|
, urls :: URL
|
2019-02-17 19:52:28 +01:00
|
|
|
, wording :: Wording
|
2019-01-27 21:41:21 +01:00
|
|
|
}
|
|
|
|
|
2020-05-08 15:51:25 +02:00
|
|
|
type Renderer m = (MonadIO m, MonadReader Blog m)
|
|
|
|
|
|
|
|
template :: Renderer m => String -> Environment -> m Text
|
|
|
|
template key environment = asks templates >>= render key environment
|
2019-02-03 22:56:21 +01:00
|
|
|
|
2019-04-28 17:27:55 +02:00
|
|
|
keepOrWarn :: Map String Article -> Either ParseError (String, Article) -> IO (Map String Article)
|
|
|
|
keepOrWarn accumulator (Left parseErrors) =
|
|
|
|
forM [show parseErrors, "=> Ignoring this article"] putStrLn
|
|
|
|
>> return accumulator
|
|
|
|
keepOrWarn accumulator (Right (key, article)) =
|
|
|
|
return $ insert key article accumulator
|
|
|
|
|
2019-02-15 14:16:21 +01:00
|
|
|
findArticles :: FilePath -> IO (Map String Article)
|
2019-02-15 14:13:43 +01:00
|
|
|
findArticles =
|
2019-02-06 17:16:52 +01:00
|
|
|
Files.find
|
2019-02-02 23:23:05 +01:00
|
|
|
>=> filterM isMarkDownFile
|
2019-02-15 14:13:43 +01:00
|
|
|
>=> mapM Article.at
|
2019-04-28 17:27:55 +02:00
|
|
|
>=> foldM keepOrWarn Map.empty
|
2019-01-27 21:41:21 +01:00
|
|
|
where
|
|
|
|
isMarkDownFile path = do
|
|
|
|
let correctExtension = takeExtension path == ".md"
|
2019-02-02 23:23:05 +01:00
|
|
|
(correctExtension &&) <$> doesFileExist path
|
2019-01-27 21:41:21 +01:00
|
|
|
|
2019-02-15 14:16:21 +01:00
|
|
|
tagged :: Collection -> FilePath -> IO (String, Set String)
|
2019-01-27 21:41:21 +01:00
|
|
|
tagged collection path = do
|
2019-02-06 17:16:52 +01:00
|
|
|
links <- Files.find path
|
2019-02-15 14:16:21 +01:00
|
|
|
keys <- forM links $ \link -> do
|
2019-02-02 23:23:05 +01:00
|
|
|
fileExists <- doesFileExist link
|
2019-02-15 14:16:21 +01:00
|
|
|
return $ if fileExists
|
2019-02-15 18:07:59 +01:00
|
|
|
then let articleKey = Article.getKey link in
|
2019-02-18 15:16:34 +01:00
|
|
|
maybe Set.empty (\_ -> Set.singleton articleKey) (lookup articleKey collection)
|
2019-02-15 14:16:21 +01:00
|
|
|
else Set.empty
|
|
|
|
return (takeFileName path, foldl Set.union Set.empty keys)
|
2019-01-27 21:41:21 +01:00
|
|
|
|
2019-02-19 21:48:55 +01:00
|
|
|
discover :: Path -> IO (Collection, Map String (Set String))
|
2019-03-02 23:40:34 +01:00
|
|
|
discover path = do
|
2019-02-15 15:11:31 +01:00
|
|
|
articles <- findArticles $ articlesPath path
|
2019-01-27 21:41:21 +01:00
|
|
|
tags <- Map.fromList . filter (not . Set.null . snd)
|
2019-02-15 15:11:31 +01:00
|
|
|
<$> (Files.find (articlesPath path </> "tags") >>= mapM (articles `tagged`))
|
2019-02-19 21:48:55 +01:00
|
|
|
return (articles, tags)
|
|
|
|
|
|
|
|
build :: Arguments -> IO Blog
|
|
|
|
build arguments = do
|
2019-12-21 12:50:38 +01:00
|
|
|
urls <- URL.build arguments
|
2020-05-08 15:51:25 +02:00
|
|
|
let hasRSS = maybe False (\_-> True) $ rss urls
|
2019-02-19 21:48:55 +01:00
|
|
|
wording <- Wording.build arguments
|
2020-05-08 15:51:25 +02:00
|
|
|
templates <- Template.build wording
|
2019-03-02 23:40:34 +01:00
|
|
|
root <- Files.absolute . Dir $ Arguments.sourceDir arguments
|
|
|
|
withCurrentDirectory root $ do
|
|
|
|
path <- Path.build root arguments
|
|
|
|
let name = maybe (takeFileName $ dropTrailingPathSeparator root) id
|
|
|
|
$ Arguments.name arguments
|
|
|
|
skin <- Skin.build name arguments
|
|
|
|
(articles, tags) <- discover path
|
2020-05-08 15:51:25 +02:00
|
|
|
return $ Blog {
|
|
|
|
articles, hasRSS, name, path, skin, tags, templates, urls, wording
|
|
|
|
}
|