rib-0.5.0.0: src/Rib/Document.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE ViewPatterns #-}
module Rib.Document
( -- * Document type
Document,
mkDocumentFrom,
-- * Document properties
documentPath,
documentVal,
documentHtml,
documentMeta,
documentUrl,
)
where
import Control.Monad.Except hiding (fail)
import Data.Aeson
import Development.Shake.FilePath ((-<.>))
import Lucid (Html)
import Named
import Path hiding ((-<.>))
import Rib.Markup
import qualified Text.Show
-- | A document written in a lightweight markup language (LML)
--
-- The type variable `repr` indicates the representation type of the Markup
-- parser to be used.
data Document repr meta
= Document
{ -- | Path to the document; relative to the source directory.
_document_path :: Path Rel File,
-- | Parsed representation of the document.
_document_val :: repr,
-- | HTML rendering of the parsed representation.
_document_html :: Html (),
-- | The parsed metadata.
_document_meta :: meta
}
deriving (Generic, Show)
documentPath :: Document repr meta -> Path Rel File
documentPath = _document_path
documentVal :: Document repr meta -> repr
documentVal = _document_val
documentHtml :: Document repr meta -> Html ()
documentHtml = _document_html
documentMeta :: Document repr meta -> meta
documentMeta = _document_meta
-- | Return the URL for the given @.html@ file under serve directory
--
-- File path must be relative to the serve directory.
--
-- You may also pass source paths as long as they map directly to destination
-- path except for file extension.
documentUrl :: Document repr meta -> Text
documentUrl doc = toText $ toFilePath ([absdir|/|] </> (documentPath doc)) -<.> ".html"
data DocumentError
= DocumentError_MarkupError Text
| DocumentError_MetadataMissing
| DocumentError_MetadataMalformed Text
instance Show DocumentError where
show = \case
DocumentError_MarkupError e -> toString e
DocumentError_MetadataMissing -> "Metadata missing"
DocumentError_MetadataMalformed msg -> "Bad metadata JSON: " <> toString msg
-- | Parse, render to HTML and extract metadata from the given file.
--
-- Return the Document type containing converted values.
mkDocumentFrom ::
forall m b repr meta.
(MonadError DocumentError m, MonadIO m, Markup repr, FromJSON meta) =>
-- | File path, used only to identify (not access) the document
"relpath" :! Path Rel File ->
-- | Actual file path, for access and reading
"path" :! Path b File ->
m (Document repr meta)
mkDocumentFrom k@(arg #relpath -> k') f = do
v <-
liftEither . first DocumentError_MarkupError
=<< readDoc k f
html <-
liftEither . first DocumentError_MarkupError $
renderDoc v
metaValue <-
liftEither . (first DocumentError_MetadataMalformed)
=<< maybeToEither DocumentError_MetadataMissing (extractMeta v)
meta <-
liftEither . first (DocumentError_MetadataMalformed . toText) $
resultToEither (fromJSON metaValue)
pure $ Document k' v html meta
where
maybeToEither e = liftEither . maybeToRight e
resultToEither = \case
Error e -> Left e
Success v -> Right v