neuron-1.0.0.0: src/lib/Neuron/Zettelkasten/Zettel/Parser.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Neuron.Zettelkasten.Zettel.Parser where
import Control.Monad.Writer
import Data.List (nub)
import Data.Some
import Data.TagTree (Tag)
import Neuron.Reader.Type
import Neuron.Zettelkasten.ID
import Neuron.Zettelkasten.Query.Error
import Neuron.Zettelkasten.Query.Parser (queryFromURILink)
import Neuron.Zettelkasten.Zettel
import qualified Neuron.Zettelkasten.Zettel.Meta as Meta
import Reflex.Dom.Pandoc.URILink (queryURILinks)
import Relude
import Text.Pandoc.Definition (Pandoc)
import Text.Pandoc.Util
parseZettel ::
ZettelFormat ->
ZettelReader ->
FilePath ->
ZettelID ->
Text ->
ZettelC
parseZettel format zreader fn zid s = do
case zreader fn s of
Left parseErr ->
Left $ Zettel zid format fn "Unknown" False [] Nothing False [] parseErr s
Right (meta, doc) ->
let -- Determine zettel title
(title, titleInBody) = case Meta.title =<< meta of
Just tit -> (tit, False)
Nothing -> fromMaybe (zettelIDRaw zid, False) $ do
((,True) . plainify . snd <$> getH1 doc)
-- Accumulate queries
(queries, errors) = runWriter $ extractQueries doc
-- Determine zettel tags
metaTags = fromMaybe [] $ Meta.tags =<< meta
queryTags = getInlineTag `mapMaybe` queries
tags = nub $ metaTags <> queryTags
-- Determine other metadata
date = Meta.date =<< meta
unlisted = fromMaybe False $ Meta.unlisted =<< meta
in Right $ Zettel zid format fn title titleInBody tags date unlisted queries errors doc
where
-- Extract all (valid) queries from the Pandoc document
extractQueries :: MonadWriter [QueryParseError] m => Pandoc -> m [Some ZettelQuery]
extractQueries doc =
fmap catMaybes $
forM (queryURILinks doc) $ \ul ->
case queryFromURILink ul of
Left e -> do
tell [e]
pure Nothing
Right v ->
pure v
getInlineTag :: Some ZettelQuery -> Maybe Tag
getInlineTag = \case
Some (ZettelQuery_TagZettel tag) -> Just tag
_ -> Nothing
-- | Like `parseZettel` but operates on multiple files.
parseZettels ::
[((ZettelFormat, ZettelReader), [(ZettelID, FilePath, Text)])] ->
[ZettelC]
parseZettels filesPerFormat =
flip concatMap filesPerFormat $ \((format, zreader), files) ->
flip fmap files $ \(zid, path, s) ->
parseZettel format zreader path zid s