{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE OverloadedStrings #-} import Control.Monad (unless, when) import Control.Monad.IO.Class import Control.Monad.Trans.State.Lazy import Data.Default (def) import Data.Foldable (traverse_) import Data.List.Extra (stripSuffix) import qualified Data.Map as M import Data.Maybe (isJust) import qualified Data.Text as T import qualified Data.Text.Encoding import qualified Data.Text.IO as TIO import qualified Data.Yaml as Y import Hakyll.Core.Util.File (getRecursiveContents, makeDirectories) import Lens.Micro import Lens.Micro.Aeson import Lens.Micro.Mtl import Lens.Micro.TH import System.Environment (getArgs) import System.FilePath ((), splitPath) import Text.Pandoc.Class (runIOorExplode) import qualified Text.Pandoc.Definition import qualified Text.Pandoc.Extensions import Text.Pandoc.Options (ReaderOptions(..)) import Text.Pandoc.Readers.Markdown (readMarkdown) import Debug.Trace io :: MonadIO m => IO a -> m a io = liftIO just _ (Just val) = val just err Nothing = error ("Missing: " ++ err) data SiteState = SiteState { _pages :: M.Map FilePath ( FilePath , Y.Value , Text.Pandoc.Definition.Pandoc -- loc -> (source, meta, markdown) ) , _redirects :: M.Map FilePath FilePath -- from -> to , _htags :: M.Map [String] [FilePath] -- tag sequence -> tagged locs , _installs :: M.Map FilePath FilePath -- file hash -> install location , _templates :: M.Map FilePath () -- TODO mustache templates , _outputDir :: FilePath } deriving (Show) makeLenses ''SiteState emptySiteState out = SiteState { _pages = M.empty , _redirects = M.empty , _htags = M.empty , _installs = M.empty , _templates = M.empty , _outputDir = out } type Site a = StateT SiteState IO a hasSuffix s = isJust . stripSuffix s sourcePages :: FilePath -> Site () sourcePages fp = do links <- io $ filter (hasSuffix ".md" . last . splitPath) <$> getRecursiveContents (pure . const False) fp traverse_ loadPage (map (fp ) links) markdownReadOpts = def { readerExtensions = Text.Pandoc.Extensions.enableExtension Text.Pandoc.Extensions.Ext_smart Text.Pandoc.Extensions.pandocExtensions } loadPage :: FilePath -> Site () loadPage fp = do txt <- io $ TIO.readFile fp (T.take 4 txt == "---\n") `unless` error ("metadata block start missing in " ++ fp) let (meta, markdown) = T.breakOn "\n---\n" (T.drop 4 txt) T.null meta `when` error ("metadata block bad in " ++ fp) yml <- Y.decodeThrow $ Data.Text.Encoding.encodeUtf8 meta md <- io $ runIOorExplode $ readMarkdown markdownReadOpts (T.drop 5 markdown) let mount = T.unpack . just ("mount point of " ++ fp) $ yml ^? key "title" . _String pages %= M.insert mount (fp, yml, md) sourceTemplates :: FilePath -> Site () sourceTemplates _ = pure () installPage :: FilePath -> Site () installPage = undefined installFile :: FilePath -> Site FilePath installFile = undefined makeRedirect :: FilePath -> FilePath -> Site () makeRedirect = undefined makeRedirects :: Site () makeRedirects = undefined renderTag :: [String] -> Site () renderTag = undefined renderTags :: Site () renderTags = undefined main = do [targetDir] <- getArgs flip runStateT (emptySiteState targetDir) $ do traverse sourceTemplates ["templates"] traverse sourcePages ["external"] get >>= io . print