{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ViewPatterns #-}
module Data.ZettelGraph where
import Data.Text
(Text)
import qualified Data.Text as T
import Text.Pandoc
import Text.Pandoc.Walk
import Data.Path
import Data.Zettel
import Data.ZettelID
import Data.ZettelMeta
import Data.ZettelPath
import Data.Maybe
(catMaybes, fromMaybe)
import System.FilePath
(takeFileName)
import System.Directory
(listDirectory)
import Control.Lens
(from, (^.))
zettelLinks :: ZettelRoot -> IO [(ZettelMeta, [ZettelID])]
zettelLinks base = do
paths <- listDirectory (toPosixPath (base ^. from _ZettelRoot))
catMaybes <$> traverse go paths
where
go :: FilePath -> IO (Maybe (ZettelMeta, [ZettelID]))
go (fromPath . file . T.pack -> Just zid) = do
zettel <- readZettel base zid
pure $ either (const Nothing) (Just . internalLinks zid) zettel
go _ = pure Nothing
extractLink :: Inline -> [ZettelID]
extractLink = \case
Link _ _ (u, _) -> catMaybes [fromPath (path u)]
_ -> []
where
path = file . T.pack . takeFileName
internalLinks :: ZettelID -> Pandoc -> (ZettelMeta, [ZettelID])
internalLinks f p = (fromMaybe unknown (zettelMeta f p), query extractLink p)
where
unknown = ZettelMeta f "No title"
dot :: [(ZettelMeta, [ZettelID])] -> Text
dot links = header <> nodes <> "\n" <> edges <> footer
where
nodes = T.unlines (map (\(f,_) -> node f) links)
edges = T.unlines (concatMap (\(f,ts) -> map (edge (zettelID f)) ts) links)
node :: ZettelMeta -> Text
node ZettelMeta{..} = render zettelID <> " [label=\""<> title <>"\"];"
edge :: ZettelID -> ZettelID -> Text
edge f t = render f <> " -> " <> render t <> ";"
header = "digraph g {\n"
footer = "}"