157 lines
4.9 KiB
Haskell
157 lines
4.9 KiB
Haskell
{-
|
|
- Copyright (C) 2023 University of Luxembourg
|
|
-
|
|
- Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
- use this file except in compliance with the License. You may obtain a copy
|
|
- of the License from the LICENSE file in this repository, or at:
|
|
-
|
|
- http://www.apache.org/licenses/LICENSE-2.0
|
|
-
|
|
- Unless required by applicable law or agreed to in writing, software
|
|
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
- License for the specific language governing permissions and limitations
|
|
- under the License.
|
|
-}
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
module Utils where
|
|
|
|
import Control.Monad (filterM, forM)
|
|
import Control.Monad.IO.Class
|
|
import Data.List.Extra (stripSuffix)
|
|
import Data.Maybe (isJust)
|
|
import qualified Data.Text as T
|
|
import Lens.Micro.Mtl
|
|
import System.Directory
|
|
( createDirectoryIfMissing
|
|
, doesDirectoryExist
|
|
, getDirectoryContents
|
|
)
|
|
import qualified System.FilePath
|
|
import System.FilePath (splitDirectories, takeDirectory)
|
|
import Text.Pandoc.Definition
|
|
import qualified Text.Pandoc.Walk
|
|
import Types
|
|
|
|
-- | A shortcut for `liftIO`.
|
|
io :: MonadIO m => IO a -> m a
|
|
io = liftIO
|
|
|
|
-- | A helper for throwing an error if something is `Nothing`
|
|
just :: String -> Maybe a -> a
|
|
just _ (Just val) = val
|
|
just err Nothing = error ("Missing: " ++ err)
|
|
|
|
-- | Test for whether something listy has a suffix
|
|
hasSuffix :: Eq a => [a] -> [a] -> Bool
|
|
hasSuffix s = isJust . stripSuffix s
|
|
|
|
-- | The same second as from arrows et al.
|
|
second :: (a -> b) -> (c, a) -> (c, b)
|
|
second f (a, b) = (a, f b)
|
|
|
|
-- | A pandoc walker for modifying the URLs.
|
|
walkURLs ::
|
|
(FilePath -> Site FilePath)
|
|
-> Text.Pandoc.Definition.Pandoc
|
|
-> Site Text.Pandoc.Definition.Pandoc
|
|
walkURLs f = Text.Pandoc.Walk.walkM go
|
|
where
|
|
go (Link a i (u, t)) = do
|
|
u' <- T.pack <$> f (T.unpack u)
|
|
pure $ Link a i (u', t)
|
|
go (Image a i (u, t)) = do
|
|
u' <- T.pack <$> f (T.unpack u)
|
|
pure $ Image a i (u', t)
|
|
go x = pure x
|
|
|
|
-- | A pandoc walker for adding the local links to the headings (links are
|
|
-- appended and get a given class)
|
|
addHeadingLinks ::
|
|
T.Text -> Text.Pandoc.Definition.Pandoc -> Text.Pandoc.Definition.Pandoc
|
|
addHeadingLinks cls = Text.Pandoc.Walk.walk go
|
|
where
|
|
go h@(Header lvl attr@(id, _, _) inlines) =
|
|
Header
|
|
lvl
|
|
attr
|
|
(inlines
|
|
++ [ Link
|
|
("", [cls], [])
|
|
[Str "#"]
|
|
("#" <> id, "Link to this section")
|
|
])
|
|
go x = x
|
|
|
|
-- | @"https://example.com" `hasUriScheme` "https"@
|
|
hasUriScheme :: String -> String -> Bool
|
|
hasUriScheme x = all id . zipWith (==) x . (++ ":")
|
|
|
|
-- | @unAbsolute "/root/path" == "root/path"@
|
|
unAbsolute :: String -> String
|
|
unAbsolute = dropWhile (== '/')
|
|
|
|
-- | Lift a function that processes `Block`s to a function that -- processes a
|
|
-- `Pandoc`.
|
|
withPandocBlocks :: ([Block] -> [Block]) -> Pandoc -> Pandoc
|
|
withPandocBlocks f (Pandoc meta blocks) = Pandoc meta (f blocks)
|
|
|
|
infixr 5 </>
|
|
|
|
a </> b@('/':_) =
|
|
error
|
|
$ "internal error: unchecked concatenation of absolute path: "
|
|
++ a
|
|
++ " </> "
|
|
++ b
|
|
a </> b = (System.FilePath.</>) a b
|
|
|
|
-- | Get all contents of a directory. (Interned from Hakyll.)
|
|
getRecursiveContents ::
|
|
(FilePath -> IO Bool) -- ^ Ignore this file/directory
|
|
-> FilePath -- ^ Directory to search
|
|
-> IO [FilePath] -- ^ List of files found
|
|
getRecursiveContents ignore top = go ""
|
|
where
|
|
isProper x
|
|
| x `elem` [".", ".."] = return False
|
|
| otherwise = not <$> ignore x
|
|
go dir = do
|
|
dirExists <- doesDirectoryExist (top </> dir)
|
|
if not dirExists
|
|
then return []
|
|
else do
|
|
names <- filterM isProper =<< getDirectoryContents (top </> dir)
|
|
paths <-
|
|
forM names $ \name -> do
|
|
let rel = dir </> name
|
|
isDirectory <- doesDirectoryExist (top </> rel)
|
|
if isDirectory
|
|
then go rel
|
|
else return [rel]
|
|
return $ concat paths
|
|
|
|
-- | A nice tool interned from Relude.
|
|
foldMapM :: (Monoid b, Monad m, Foldable f) => (a -> m b) -> f a -> m b
|
|
foldMapM f xs = foldr step return xs mempty
|
|
where
|
|
step x r z = f x >>= \y -> r $! z `mappend` y
|
|
|
|
-- | Source paths from a source-y directory. The paths that have to be ignored
|
|
-- by config `notSourceDirs` are omitted.
|
|
sourcePaths :: Monoid a => FilePath -> (FilePath -> Site a) -> Site a
|
|
sourcePaths fp process = do
|
|
notSource <- use notSourceDirs
|
|
let ignoreDir ds
|
|
| null ds = False
|
|
| last ds `elem` notSource = True
|
|
| otherwise = False
|
|
io (getRecursiveContents (pure . ignoreDir . splitDirectories) fp)
|
|
>>= foldMapM process
|
|
|
|
-- | Given a path to a file, try to make the path writable by making all
|
|
-- directories on the path. (Interned from Hakyll.)
|
|
makeDirectories :: FilePath -> IO ()
|
|
makeDirectories = createDirectoryIfMissing True . takeDirectory
|