ema-0.10.0.0: src/Ema/Route/Prism/Check.hs
module Ema.Route.Prism.Check (
checkRoutePrismGivenFilePath,
checkRoutePrismGivenRoute,
) where
import Control.Monad.Writer (Writer, runWriter, tell)
import Data.Text qualified as T
import Ema.Route.Prism.Type (Prism_, fromPrism_)
import Optics.Core (Prism', preview, review)
import System.FilePath ((</>))
checkRoutePrismGivenRoute ::
(HasCallStack, Eq r, Show r) =>
(a -> Prism_ FilePath r) ->
a ->
r ->
Either Text ()
checkRoutePrismGivenRoute enc a r =
let s = review (fromPrism_ $ enc a) r
in checkRoutePrism enc a r s
checkRoutePrismGivenFilePath ::
(HasCallStack, Eq r, Show r) =>
(a -> Prism_ FilePath r) ->
a ->
FilePath ->
Either (r, [(FilePath, Text)]) (Maybe r)
checkRoutePrismGivenFilePath enc a s = do
-- We should treat /foo, /foo.html and /foo/index.html as equivalent.
let candidates = [s, s <> ".html", s </> "index.html"]
rp = fromPrism_ $ enc a
case asum (preview rp <$> candidates) of
Nothing -> pure Nothing
Just r -> do
-- All candidates must be checked, and if even one passes - we let this
-- route go through.
let (failed, passed) =
partitionEithers $
candidates <&> \candidate ->
case checkRoutePrism enc a r candidate of
Left err -> Left (candidate, err)
Right () -> Right ()
if null passed
then Left (r, failed)
else Right (Just r)
checkRoutePrism :: (Eq r, Show r) => (a -> Prism_ FilePath r) -> a -> r -> FilePath -> Either Text ()
checkRoutePrism p a r s =
let (valid, checkLog) =
runWriter $ routePrismIsLawfulFor p a r s
in if valid
then Right ()
else Left $ "Unlawful route prism for route value '" <> show r <> "'\n- " <> T.intercalate "\n- " checkLog
{- | Check if the route @Prism_@ is lawful.
A route @Prism_@ is lawful if its conversions both the ways form an
isomorphism for a given value.
Returns a Writer reporting logs.
-}
routePrismIsLawfulFor ::
forall ctx a.
(Eq a, Show a) =>
(ctx -> Prism_ FilePath a) ->
ctx ->
a ->
FilePath ->
Writer [Text] Bool
routePrismIsLawfulFor enc =
prismIsLawfulFor . fromPrism_ . enc
prismIsLawfulFor ::
forall s a.
(Eq a, Eq s, Show a, ToText s) =>
Prism' s a ->
a ->
s ->
Writer [Text] Bool
prismIsLawfulFor p a s = do
-- TODO: The logging here could be improved.
-- log $ "Testing Partial ISO law for " <> show a <> " and " <> toText s
let s' :: s = review p a
-- log $ "Prism actual encoding: " <> toText s'
let ma' :: Maybe a = preview p s'
-- log $ "Decoding of that encoding: " <> show ma'
unless (s == s') $
log $
toText s <> " /= " <> toText s' <> " (encoding of '" <> show a <> "')"
unless (Just a == ma') $
log $
show (Just a) <> " /= " <> show ma' <> " (decoding of " <> toText s <> ")"
pure $ (s == s') && (Just a == ma')
where
log = tell . one