-- | Given a dependency graph, this module provides a function that will -- generate an order in which the graph can be visited, so that all the -- dependencies of a given node have been visited before the node itself is -- visited. -- module Hakyll.Core.DirectedGraph.DependencySolver ( solveDependencies ) where import Prelude import qualified Prelude as P import Data.Set (Set) import Data.Maybe (mapMaybe) import qualified Data.Map as M import qualified Data.Set as S import Hakyll.Core.DirectedGraph import Hakyll.Core.DirectedGraph.Internal -- | Solve a dependency graph. This function returns an order to run the -- different nodes -- solveDependencies :: Ord a => DirectedGraph a -- ^ Graph -> [a] -- ^ Resulting plan solveDependencies = P.reverse . order [] [] S.empty -- | Produce a reversed order using a stack -- order :: Ord a => [a] -- ^ Temporary result -> [Node a] -- ^ Backtrace stack -> Set a -- ^ Items in the stack -> DirectedGraph a -- ^ Graph -> [a] -- ^ Ordered result order temp stack set graph@(DirectedGraph graph') -- Empty graph - return our current result | M.null graph' = temp | otherwise = case stack of -- Empty stack - pick a node, and add it to the stack [] -> let (tag, node) = M.findMin graph' in order temp (node : stack) (S.insert tag set) graph -- At least one item on the stack - continue using this item (node : stackTail) -> -- Check which dependencies are still in the graph let tag = nodeTag node deps = S.toList $ nodeNeighbours node unsatisfied = mapMaybe (`M.lookup` graph') deps in case unsatisfied of -- All dependencies for node are satisfied, we can return it and -- remove it from the graph [] -> order (tag : temp) stackTail (S.delete tag set) (DirectedGraph $ M.delete tag graph') -- There is at least one dependency left. We need to solve that -- one first... (dep : _) -> if nodeTag dep `S.member` set -- The dependency is already in our stack - cycle detected! then cycleError -- Continue with the dependency else order temp (dep : node : stackTail) (S.insert (nodeTag dep) set) graph where cycleError = error $ "Hakyll.Core.DirectedGraph.DependencySolver.order: " ++ "Cycle detected!" -- TODO: Dump cycle