packages feed

croque-mort-0.1.0.0: app/FileSystem.hs

{-# LANGUAGE TupleSections #-}

module FileSystem
  ( parseLinks,

    -- * Tests
    pureParseLinks,
  )
where

import qualified Data.Map.Strict as Map
import Data.Maybe (fromMaybe, mapMaybe)
import Data.Poolboy (defaultPoolboySettings)
import Data.Poolboy.Tactics (concurrentRecursive')
import qualified Data.Text as T
import qualified Data.Text.IO as T
import Domain
import System.Directory (doesFileExist, listDirectory)
import System.FilePath (takeExtension, (</>))
import Text.HTML.Scalpel

parseLinks :: [(RemoteBaseURL, Folder)] -> IO ParsedLinks
parseLinks targets = do
  let go ::
        (RemoteBaseURL, Folder, FilePath) ->
        IO ([(RemoteBaseURL, Folder, FilePath)], [(RemoteBaseURL, WithRootFolder (Map.Map FilePath [Link]))])
      go (remoteBase, root, path) = do
        isFile <- doesFileExist path
        if isFile
          then do
            links <-
              if takeExtension path == ".html"
                then pureParseLinks <$> T.readFile path
                else return []
            return ([], [(remoteBase, WithRootFolder root $ Map.singleton path links)])
          else do
            entries <- listDirectory path
            return ((remoteBase,root,) . (path </>) <$> entries, [])
  ParsedLinks . Map.fromListWith (\x y -> WithRootFolder x.root $ x.links <> y.links) . concat
    <$> concurrentRecursive'
      defaultPoolboySettings
      (map go)
      ((map $ \(remoteBase, root) -> go (remoteBase, root, root.unFolder)) targets)

pureParseLinks :: T.Text -> [Link]
pureParseLinks input = fromMaybe [] $ scrapeStringLike input getLinks
  where
    getLinks :: Scraper T.Text [Link]
    getLinks = do
      meta <- chroot "head" $ attrs "href" "link"
      body <- chroot "body" $ (<>) <$> attrs "href" "a" <*> attrs "img" "src"
      script <- attrs "src" "script"
      return $ concatMap (mapMaybe parseLink) [meta, body, script]