hablo/src/Blog.hs

78 lines
2.3 KiB
Haskell

{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE FlexibleContexts #-}
module Blog (
Blog(..)
, Skin(..)
, build
, get
) where
import Arguments (Arguments(sourceDir))
import qualified Arguments (name)
import Article (Article)
import qualified Article (at, key)
import Blog.Skin (Skin(..))
import qualified Blog.Skin as Skin (build)
import Control.Monad ((>=>), filterM, forM)
import Control.Monad.Reader (MonadReader, ask)
import Data.Either (rights)
import Data.Map (Map)
import qualified Data.Map as Map (fromList, member)
import Data.Set (Set)
import qualified Data.Set as Set (empty, null, singleton, union)
import qualified Files (find)
import System.Directory (doesFileExist, withCurrentDirectory)
import System.FilePath ((</>), dropTrailingPathSeparator, splitFileName, takeExtension, takeFileName)
type Collection = Map String Article
data Blog = Blog {
articles :: Collection
, name :: String
, root :: FilePath
, skin :: Skin
, tags :: Map String (Set String)
}
get :: MonadReader Blog m => (Blog -> a) -> m a
get = (<$> ask)
findArticles :: FilePath -> IO (Map String Article)
findArticles =
Files.find
>=> filterM isMarkDownFile
>=> mapM Article.at
>=> return . Map.fromList . rights
where
isMarkDownFile path = do
let correctExtension = takeExtension path == ".md"
(correctExtension &&) <$> doesFileExist path
tagged :: Collection -> FilePath -> IO (String, Set String)
tagged collection path = do
links <- Files.find path
keys <- forM links $ \link -> do
fileExists <- doesFileExist link
return $ if fileExists
then let articleKey = Article.key link in
if Map.member articleKey collection then Set.singleton articleKey else Set.empty
else Set.empty
return (takeFileName path, foldl Set.union Set.empty keys)
build :: Arguments -> IO Blog
build arguments = withCurrentDirectory root $ do
let name = maybe (takeFileName $ dropTrailingPathSeparator root) id $ Arguments.name arguments
skin <- Skin.build name arguments
articles <- findArticles articlesPath
tags <- Map.fromList . filter (not . Set.null . snd)
<$> (Files.find (articlesPath </> "tags") >>= mapM (articles `tagged`))
return $ Blog {
articles
, name
, root
, skin
, tags
}
where
(root, articlesPath) = splitFileName $ sourceDir arguments