@@ -1,7 +1,6 @@
1--------------------------------------------------------------------------------
1--------------------------------------------------------------------------------
2-- | Internally used compiler module
2-- | Internally used compiler module
3{-# LANGUAGE CPP #-}
3{-# LANGUAGE CPP #-}
4−{-# LANGUAGE DeriveFunctor #-}
5{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE GADTs #-}
5{-# LANGUAGE GADTs #-}
7{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6{-# LANGUAGE GeneralizedNewtypeDeriving #-}
@@ -11,26 +10,19 @@ module Hakyll.Core.Compiler.Internal
12 , CompilerRead (..)
11 , CompilerRead (..)
13 , CompilerWrite (..)
12 , CompilerWrite (..)
14− , CompilerErrors (..)
15 , CompilerResult (..)
13 , CompilerResult (..)
16 , Compiler (..)
14 , Compiler (..)
17 , runCompiler
15 , runCompiler
19 -- * Core operations
17 -- * Core operations
21 , compilerTell
18 , compilerTell
22 , compilerAsk
19 , compilerAsk
25− -- * Error operations
26 , compilerThrow
20 , compilerThrow
28 , compilerCatch
21 , compilerCatch
29− , compilerTry
22+ , compilerResult
30− , compilerErrorMessages
23+ , compilerUnsafeIO
32 -- * Utilities
25 -- * Utilities
33− , compilerDebugEntries
34 , compilerTellDependencies
26 , compilerTellDependencies
35 , compilerTellCacheHits
27 , compilerTellCacheHits
@@ -40,9 +32,7 @@ module Hakyll.Core.Compiler.Internal
40import Control.Applicative (Alternative (..))
32import Control.Applicative (Alternative (..))
41import Control.Exception (SomeException, handle)
33import Control.Exception (SomeException, handle)
42import Control.Monad (forM_)
34import Control.Monad (forM_)
43−import Control.Monad.Except (MonadError (..))
35+import Control.Monad.Except (MonadError (..))
44−import Data.List.NonEmpty (NonEmpty (..))
45−import qualified Data.List.NonEmpty as NonEmpty
46#if MIN_VERSION_base(4,9,0)
36#if MIN_VERSION_base(4,9,0)
47import Data.Semigroup (Semigroup (..))
37import Data.Semigroup (Semigroup (..))
@@ -55,6 +45,7 @@ import Hakyll.Core.Configuration
55import Hakyll.Core.Dependencies
45import Hakyll.Core.Dependencies
56import Hakyll.Core.Identifier
46import Hakyll.Core.Identifier
57import Hakyll.Core.Identifier.Pattern
47import Hakyll.Core.Identifier.Pattern
48+import Hakyll.Core.Logger (Logger)
58import qualified Hakyll.Core.Logger as Logger
49import qualified Hakyll.Core.Logger as Logger
59import Hakyll.Core.Metadata
50import Hakyll.Core.Metadata
60import Hakyll.Core.Provider
51import Hakyll.Core.Provider
@@ -84,7 +75,7 @@ data CompilerRead = CompilerRead
84 , -- | Compiler store
75 , -- | Compiler store
85 compilerStore :: Store
76 compilerStore :: Store
86 , -- | Logger
77 , -- | Logger
87− compilerLogger :: Logger.Logger
78+ compilerLogger :: Logger
@@ -113,29 +104,11 @@ instance Monoid CompilerWrite where
115--------------------------------------------------------------------------------
106--------------------------------------------------------------------------------
116−-- | Distinguishes reasons in a 'CompilerError'
107+data CompilerResult a where
117−data CompilerErrors a
118− -- | One or more exceptions occured during compilation
119− = CompilationFailure (NonEmpty a)
120− -- | Absence of any result, most notably in template contexts. May still
121− -- have error messages.
122− | CompilationNoResult [a]
126−-- | Unwrap a `CompilerErrors`
127−compilerErrorMessages :: CompilerErrors a -> [a]
128−compilerErrorMessages (CompilationFailure x) = NonEmpty.toList x
129−compilerErrorMessages (CompilationNoResult x) = x
132−--------------------------------------------------------------------------------
133−-- | An intermediate result of a compilation step
134−data CompilerResult a
135− = CompilerDone a CompilerWrite
108+ CompilerDone :: a -> CompilerWrite -> CompilerResult a
136− | CompilerSnapshot Snapshot (Compiler a)
109+ CompilerSnapshot :: Snapshot -> Compiler a -> CompilerResult a
110+ CompilerError :: [String] -> CompilerResult a
137− | CompilerRequire (Identifier, Snapshot) (Compiler a)
111+ CompilerRequire :: (Identifier, Snapshot) -> Compiler a -> CompilerResult a
138− | CompilerError (CompilerErrors String)
141--------------------------------------------------------------------------------
114--------------------------------------------------------------------------------
@@ -153,14 +126,14 @@ instance Functor Compiler where
153 return $ case res of
126 return $ case res of
154 CompilerDone x w -> CompilerDone (f x) w
127 CompilerDone x w -> CompilerDone (f x) w
155 CompilerSnapshot s c' -> CompilerSnapshot s (fmap f c')
128 CompilerSnapshot s c' -> CompilerSnapshot s (fmap f c')
156− CompilerRequire i c' -> CompilerRequire i (fmap f c')
157 CompilerError e -> CompilerError e
129 CompilerError e -> CompilerError e
130+ CompilerRequire i c' -> CompilerRequire i (fmap f c')
158 {-# INLINE fmap #-}
131 {-# INLINE fmap #-}
161--------------------------------------------------------------------------------
134--------------------------------------------------------------------------------
162instance Monad Compiler where
135instance Monad Compiler where
163− return x = compilerResult $ CompilerDone x mempty
136+ return x = Compiler $ \_ -> return $ CompilerDone x mempty
164 {-# INLINE return #-}
137 {-# INLINE return #-}
166 Compiler c >>= f = Compiler $ \r -> do
139 Compiler c >>= f = Compiler $ \r -> do
@@ -173,14 +146,14 @@ instance Monad Compiler where
173 CompilerSnapshot s c' -> CompilerSnapshot s $ do
146 CompilerSnapshot s c' -> CompilerSnapshot s $ do
174 compilerTell w -- Save dependencies!
147 compilerTell w -- Save dependencies!
149+ CompilerError e -> CompilerError e
176 CompilerRequire i c' -> CompilerRequire i $ do
150 CompilerRequire i c' -> CompilerRequire i $ do
177 compilerTell w -- Save dependencies!
151 compilerTell w -- Save dependencies!
179− CompilerError e -> CompilerError e
181 CompilerSnapshot s c' -> return $ CompilerSnapshot s (c' >>= f)
154 CompilerSnapshot s c' -> return $ CompilerSnapshot s (c' >>= f)
182− CompilerRequire i c' -> return $ CompilerRequire i (c' >>= f)
183 CompilerError e -> return $ CompilerError e
155 CompilerError e -> return $ CompilerError e
156+ CompilerRequire i c' -> return $ CompilerRequire i (c' >>= f)
184 {-# INLINE (>>=) #-}
157 {-# INLINE (>>=) #-}
186 fail = compilerThrow . return
159 fail = compilerThrow . return
@@ -197,145 +170,87 @@ instance Applicative Compiler where
199--------------------------------------------------------------------------------
172--------------------------------------------------------------------------------
200−-- | Access provided metadata from anywhere
201instance MonadMetadata Compiler where
173instance MonadMetadata Compiler where
202 getMetadata = compilerGetMetadata
174 getMetadata = compilerGetMetadata
203 getMatches = compilerGetMatches
175 getMatches = compilerGetMatches
206--------------------------------------------------------------------------------
178--------------------------------------------------------------------------------
207−-- | Compilation may fail with multiple error messages.
208−-- 'catchError' handles errors from 'throwError', 'fail' and 'Hakyll.Core.Compiler.noResult'
209instance MonadError [String] Compiler where
179instance MonadError [String] Compiler where
210− throwError = compilerThrow
180+ throwError = compilerThrow
211− catchError c = compilerCatch c . (. compilerErrorMessages)
181+ catchError = compilerCatch
214--------------------------------------------------------------------------------
184--------------------------------------------------------------------------------
215−-- | Like 'unCompiler' but treating IO exceptions as 'CompilerError's
216runCompiler :: Compiler a -> CompilerRead -> IO (CompilerResult a)
185runCompiler :: Compiler a -> CompilerRead -> IO (CompilerResult a)
217runCompiler compiler read' = handle handler $ unCompiler compiler read'
186runCompiler compiler read' = handle handler $ unCompiler compiler read'
219 handler :: SomeException -> IO (CompilerResult a)
188 handler :: SomeException -> IO (CompilerResult a)
220− handler e = return $ CompilerError $ CompilationFailure $ show e :| []
189+ handler e = return $ CompilerError [show e]
223--------------------------------------------------------------------------------
192--------------------------------------------------------------------------------
224−-- | Trying alternative compilers if the first fails, regardless whether through
225−-- 'fail', 'throwError' or 'Hakyll.Core.Compiler.noResult'.
226−-- Aggregates error messages if all fail.
227instance Alternative Compiler where
193instance Alternative Compiler where
228− empty = compilerNoResult []
194+ empty = compilerThrow []
229− x <|> y = x `compilerCatch` (\rx -> y `compilerCatch` (\ry ->
195+ x <|> y = compilerCatch x $ \es -> do
230− case (rx, ry) of
196+ logger <- compilerLogger <$> compilerAsk
231− (CompilationFailure xs, CompilationFailure ys) ->
197+ forM_ es $ \e -> compilerUnsafeIO $ Logger.debug logger $
232− compilerThrow $ NonEmpty.toList xs ++ NonEmpty.toList ys
233− (CompilationFailure xs, CompilationNoResult ys) ->
234− debug ys >> compilerThrow (NonEmpty.toList xs)
235− (CompilationNoResult xs, CompilationFailure ys) ->
236− debug xs >> compilerThrow (NonEmpty.toList ys)
237− (CompilationNoResult xs, CompilationNoResult ys) -> compilerNoResult $ xs ++ ys
240− debug = compilerDebugEntries "Hakyll.Core.Compiler.Internal: Alternative fail suppressed"
198+ "Hakyll.Core.Compiler.Internal: Alternative failed: " ++ e
241 {-# INLINE (<|>) #-}
200 {-# INLINE (<|>) #-}
244--------------------------------------------------------------------------------
203--------------------------------------------------------------------------------
245−-- | Put the result back in a compiler
246−compilerResult :: CompilerResult a -> Compiler a
247−compilerResult x = Compiler $ \_ -> return x
248−{-# INLINE compilerResult #-}
251−--------------------------------------------------------------------------------
252−-- | Get the current environment
253compilerAsk :: Compiler CompilerRead
204compilerAsk :: Compiler CompilerRead
254compilerAsk = Compiler $ \r -> return $ CompilerDone r mempty
205compilerAsk = Compiler $ \r -> return $ CompilerDone r mempty
255{-# INLINE compilerAsk #-}
206{-# INLINE compilerAsk #-}
258--------------------------------------------------------------------------------
209--------------------------------------------------------------------------------
259−-- | Put a 'CompilerWrite'
260compilerTell :: CompilerWrite -> Compiler ()
210compilerTell :: CompilerWrite -> Compiler ()
261−compilerTell = compilerResult . CompilerDone ()
211+compilerTell deps = Compiler $ \_ -> return $ CompilerDone () deps
262{-# INLINE compilerTell #-}
212{-# INLINE compilerTell #-}
265--------------------------------------------------------------------------------
215--------------------------------------------------------------------------------
266−-- | Run an IO computation without dependencies in a Compiler
267−compilerUnsafeIO :: IO a -> Compiler a
268−compilerUnsafeIO io = Compiler $ \_ -> do
270− return $ CompilerDone x mempty
271−{-# INLINE compilerUnsafeIO #-}
274−--------------------------------------------------------------------------------
275−-- | Throw errors in the 'Compiler'.
277−-- If no messages are given, this is considered a 'CompilationNoResult' error.
278−-- Otherwise, it is treated as a proper compilation failure.
279compilerThrow :: [String] -> Compiler a
216compilerThrow :: [String] -> Compiler a
280−compilerThrow = compilerResult . CompilerError .
217+compilerThrow es = Compiler $ \_ -> return $ CompilerError es
281− maybe (CompilationNoResult []) CompilationFailure .
218+{-# INLINE compilerThrow #-}
284−-- | Put a 'CompilerError' with multiple messages as 'CompilationNoResult'
285−compilerNoResult :: [String] -> Compiler a
286−compilerNoResult = compilerResult . CompilerError . CompilationNoResult
289--------------------------------------------------------------------------------
221--------------------------------------------------------------------------------
290−-- | Allows to distinguish 'CompilerError's and branch on them with 'Either'
292−-- prop> compilerTry = (`compilerCatch` return . Left) . fmap Right
293−compilerTry :: Compiler a -> Compiler (Either (CompilerErrors String) a)
294−compilerTry (Compiler x) = Compiler $ \r -> do
297− CompilerDone res' w -> return (CompilerDone (Right res') w)
298− CompilerSnapshot s c -> return (CompilerSnapshot s (compilerTry c))
299− CompilerRequire i c -> return (CompilerRequire i (compilerTry c))
300− CompilerError e -> return (CompilerDone (Left e) mempty)
301−{-# INLINE compilerTry #-}
304−--------------------------------------------------------------------------------
305−-- | Allows you to recover from 'CompilerError's.
306−-- Uses the same parameter order as 'catchError' so that it can be used infix.
308−-- prop> c `compilerCatch` f = compilerTry c >>= either f return
309−compilerCatch :: Compiler a -> (CompilerErrors String -> Compiler a) -> Compiler a
222+compilerCatch :: Compiler a -> ([String] -> Compiler a) -> Compiler a
310compilerCatch (Compiler x) f = Compiler $ \r -> do
223compilerCatch (Compiler x) f = Compiler $ \r -> do
311 res <- x r
224 res <- x r
312 case res of
225 case res of
313 CompilerDone res' w -> return (CompilerDone res' w)
226 CompilerDone res' w -> return (CompilerDone res' w)
314 CompilerSnapshot s c -> return (CompilerSnapshot s (compilerCatch c f))
227 CompilerSnapshot s c -> return (CompilerSnapshot s (compilerCatch c f))
315− CompilerRequire i c -> return (CompilerRequire i (compilerCatch c f))
316 CompilerError e -> unCompiler (f e) r
228 CompilerError e -> unCompiler (f e) r
229+ CompilerRequire i c -> return (CompilerRequire i (compilerCatch c f))
317{-# INLINE compilerCatch #-}
230{-# INLINE compilerCatch #-}
320--------------------------------------------------------------------------------
233--------------------------------------------------------------------------------
321−compilerDebugLog :: [String] -> Compiler ()
234+-- | Put the result back in a compiler
322−compilerDebugLog ms = do
235+compilerResult :: CompilerResult a -> Compiler a
323− logger <- compilerLogger <$> compilerAsk
236+compilerResult x = Compiler $ \_ -> return x
324− compilerUnsafeIO $ forM_ ms $ Logger.debug logger
237+{-# INLINE compilerResult #-}
326--------------------------------------------------------------------------------
240--------------------------------------------------------------------------------
327−-- | Pass a list of messages with a heading to the debug logger
241+compilerUnsafeIO :: IO a -> Compiler a
328−compilerDebugEntries :: String -> [String] -> Compiler ()
242+compilerUnsafeIO io = Compiler $ \_ -> do
329−compilerDebugEntries msg = compilerDebugLog . (msg:) . map indent
243+ x <- io
330− where
244+ return $ CompilerDone x mempty
331− indent = unlines . map (" "++) . lines
245+{-# INLINE compilerUnsafeIO #-}
334--------------------------------------------------------------------------------
248--------------------------------------------------------------------------------
335compilerTellDependencies :: [Dependency] -> Compiler ()
249compilerTellDependencies :: [Dependency] -> Compiler ()
336compilerTellDependencies ds = do
250compilerTellDependencies ds = do
337− compilerDebugLog $ map (\d ->
251+ logger <- compilerLogger <$> compilerAsk
252+ forM_ ds $ \d -> compilerUnsafeIO $ Logger.debug logger $
338− "Hakyll.Core.Compiler.Internal: Adding dependency: " ++ show d) ds
253+ "Hakyll.Core.Compiler.Internal: Adding dependency: " ++ show d
339 compilerTell mempty {compilerDependencies = ds}
254 compilerTell mempty {compilerDependencies = ds}
340{-# INLINE compilerTellDependencies #-}
255{-# INLINE compilerTellDependencies #-}