module Shakebook.Data where import Control.Comonad.Cofree import Control.Comonad.Store import Control.Comonad.Store.Zipper import Control.Monad.Extra import Data.Aeson as A import Development.Shake as S import Development.Shake.FilePath import RIO hiding (view) import RIO.List import qualified RIO.Text as T import Slick import Slick.Pandoc import Shakebook.Conventions import Shakebook.Zipper import Text.Pandoc.Options type ToC = Cofree [] String -- Get a JSON Value of Markdown Data with markdown body as "contents" field -- and the srcPath as "srcPath" field. readMarkdownFile' :: ReaderOptions -> WriterOptions -> String -> Action Value readMarkdownFile' readerOptions writerOptions srcPath = do docContent <- readFile' srcPath docData <- markdownToHTMLWithOpts readerOptions writerOptions . T.pack $ docContent return $ withSrcPath (T.pack srcPath) docData loadIfExists :: (FilePath -> Action Value) -> FilePath -> Action Value loadIfExists f src = ifM (S.doesFileExist src) (f src) (return (Object mempty)) getDirectoryMarkdown :: ReaderOptions -> WriterOptions -> FilePath -> [FilePattern] -> Action [Value] getDirectoryMarkdown readOpts writeOpts dir pat = do getDirectoryFiles dir pat >>= mapM (readMarkdownFile' readOpts writeOpts . (dir )) getEnrichedMarkdown :: ReaderOptions -> WriterOptions -> (Value -> Value) -> FilePath -> [FilePattern] -> Action [Value] getEnrichedMarkdown readOpts writeOpts f dir pat = fmap f <$> getDirectoryMarkdown readOpts writeOpts dir pat genBuildPageAction :: FilePath -- The HTML template -> (FilePath -> Action Value) -- How to get an initial markdown JSON Object from the out filepath. -> (Value -> Value) -- Additional modifiers for the value -> FilePath -- The out filepath -> Action Value genBuildPageAction template getData withData out = do pageT <- compileTemplate' template dataT <- withData <$> getData out writeFile' out . T.unpack $ substitute pageT dataT return dataT genIndexPageData :: [Value] -> Text -> (Text -> Text) -> Int -> Maybe (Zipper [] Value) genIndexPageData xs g h n = fmap (extend (genPageData g h)) $ paginate n xs traverseToSnd :: Functor f => (a -> f b) -> a -> f (a, b) traverseToSnd f a = (a,) <$> f a lower :: Cofree [] Value -> [Value] lower (_ :< xs) = extract <$> xs data SbConfig = SbConfig { sbSrcDir :: FilePath , sbOutDir :: FilePath , sbBaseUrl :: Text , sbMdRead :: ReaderOptions , sbHTWrite :: WriterOptions , sbPPP :: Int } deriving (Show) newtype Shakebook a = Shakebook ( ReaderT SbConfig Rules a ) deriving (Functor, Applicative, Monad, MonadReader SbConfig) newtype ShakebookA a = ShakebookA ( ReaderT SbConfig Action a ) deriving (Functor, Applicative, Monad, MonadReader SbConfig) runShakebook :: SbConfig -> Shakebook a -> Rules a runShakebook c (Shakebook f) = runReaderT f c runShakebookA :: SbConfig -> ShakebookA a -> Action a runShakebookA c (ShakebookA f) = runReaderT f c loadSortFilterEnrich :: Ord b => [FilePattern] -- Filepattern -> (Value -> b) -- A value to sortOn e.g (Down . viewPostTime) -> (Value -> Bool) -- A filtering predicate e.g (elem tag . viewTags) -> (Value -> Value) -- An initial enrichment. This is pure so can only be data derived from the initial markdown. -> ShakebookA [(String, Value)] loadSortFilterEnrich pat s f e = ShakebookA $ ask >>= \SbConfig {..} -> lift $ do allPosts <- getDirectoryFiles sbSrcDir $ map (-<.> ".md") pat readPosts <- sequence $ traverseToSnd (readMarkdownFile' sbMdRead sbHTWrite . (sbSrcDir )) <$> allPosts return $ fmap (second e) $ sortOn (s . snd) $ filter (f . snd) $ readPosts loadSortEnrich :: Ord b => [FilePattern] -> (Value -> b) -> (Value -> Value) -> ShakebookA [(String, Value)] loadSortEnrich pat s e = loadSortFilterEnrich pat s (const True) e