@@ -6,7 +6,7 @@
6module Obelisk.Migration where
6module Obelisk.Migration where
8import Control.Monad (forM, forM_, unless, void)
8import Control.Monad (forM, forM_, unless, void)
9−import Control.Monad.Catch (Exception, MonadThrow, throwM)
9+import Control.Monad.Except
10import Control.Monad.IO.Class (MonadIO, liftIO)
10import Control.Monad.IO.Class (MonadIO, liftIO)
11import Control.Monad.State.Strict (evalStateT, get, put)
11import Control.Monad.State.Strict (evalStateT, get, put)
12import Data.Map (Map)
12import Data.Map (Map)
@@ -34,36 +34,16 @@ data Migration action = Migration
34-- A path is a list of edges
34-- A path is a list of edges
35type Path = [(Hash, Hash)]
35type Path = [(Hash, Hash)]
37−data GraphInternalError = GraphInternalError Text
38+ = GraphInternalError Text
40−data CyclicGraphError = CyclicGraphError
39+ | CyclicGraphError
43−data MultipleTerminalVertices = MultipleTerminalVertices
40+ | MultipleTerminalVertices
46−data NoTerminalVertex = NoTerminalVertex
41+ | NoTerminalVertex
49−data NonEquivalentPaths = NonEquivalentPaths Hash Hash
42+ | NonEquivalentPaths Hash Hash
52−data MissingEdgeInActionMap = MissingEdgeInActionMap (Hash, Hash)
43+ | MissingEdgeInActionMap (Hash, Hash)
56− = NonLinearGraph_MultipleAdjacents Hash
44+ | NonLinearGraph_MultipleAdjacents Hash
57 | NonLinearGraph_NotConnected (Set Hash)
45 | NonLinearGraph_NotConnected (Set Hash)
58− deriving Show
46+ deriving (Eq, Ord, Show)
60−instance Exception GraphInternalError
61−instance Exception CyclicGraphError
62−instance Exception MultipleTerminalVertices
63−instance Exception NoTerminalVertex
64−instance Exception NonEquivalentPaths
65−instance Exception MissingEdgeInActionMap
66−instance Exception NonLinearGraph
68class Action action where
48class Action action where
69 parseEdgeMeta :: String -> action
49 parseEdgeMeta :: String -> action
@@ -73,10 +53,10 @@ instance Action Text where
74-- | Get the directed-acylic graph of migration
54-- | Get the directed-acylic graph of migration
75-- TODO: Consider calling getDag only when reading the graph for efficiency.
55-- TODO: Consider calling getDag only when reading the graph for efficiency.
76−getDag :: (MonadThrow m, Ord a) => AdjacencyMap a -> m (AdjacencyMap a)
56+getDag :: (MonadError MigrationError m, Ord a) => AdjacencyMap a -> m (AdjacencyMap a)
77getDag g = case topSort g of
57getDag g = case topSort g of
78 Just _ -> pure g
58 Just _ -> pure g
79− Nothing -> throwM CyclicGraphError
59+ Nothing -> throwError CyclicGraphError
81-- | Read the graph from a directory of files.
61-- | Read the graph from a directory of files.
82readGraph :: Action action => FilePath -> Text -> IO (Maybe (Migration action))
62readGraph :: Action action => FilePath -> Text -> IO (Maybe (Migration action))
@@ -111,7 +91,7 @@ readGraph root name = doesDirectoryExist root >>= \case
112-- Complexity: O(V+E)
92-- Complexity: O(V+E)
113ensureGraphIntegrity
93ensureGraphIntegrity
114− :: (MonadThrow m, Monoid action, Ord action)
94+ :: (MonadError MigrationError m, Monoid action, Ord action)
115 => Migration action -> m ()
95 => Migration action -> m ()
116ensureGraphIntegrity m = do
96ensureGraphIntegrity m = do
117 graph <- fmap adjacencyMap $ getDag $ _migration_graph m
97 graph <- fmap adjacencyMap $ getDag $ _migration_graph m
@@ -125,7 +105,7 @@ ensureGraphIntegrity m = do
125 Nothing -> pure $ Map.insert adj acc' visited
105 Nothing -> pure $ Map.insert adj acc' visited
126 Just adjAcc -> if acc' == adjAcc
106 Just adjAcc -> if acc' == adjAcc
127 then pure visited
107 then pure visited
128− else throwM $ NonEquivalentPaths start adj
108+ else throwError $ NonEquivalentPaths start adj
129 visitFrom visited' acc' adj
109 visitFrom visited' acc' adj
130 -- We only check all paths from the _first_ vertex; it is not necessary to
110 -- We only check all paths from the _first_ vertex; it is not necessary to
131 -- check all paths between _all_ pairs of vertices as that can be proved by
111 -- check all paths between _all_ pairs of vertices as that can be proved by
@@ -136,23 +116,23 @@ ensureGraphIntegrity m = do
136-- | Ensure that the graph is a fully connected linear list
116-- | Ensure that the graph is a fully connected linear list
138-- Returns the last vertex in the linear list.
118-- Returns the last vertex in the linear list.
139−ensureGraphLinearity :: MonadThrow m => Migration action -> m Hash
119+ensureGraphLinearity :: MonadError MigrationError m => Migration action -> m Hash
140ensureGraphLinearity m = do
120ensureGraphLinearity m = do
141 graph <- fmap adjacencyMap $ getDag $ _migration_graph m
121 graph <- fmap adjacencyMap $ getDag $ _migration_graph m
142 firstVertex <- getFirst $ _migration_graph m
122 firstVertex <- getFirst $ _migration_graph m
144 allVertices = vertexSet $ _migration_graph m
124 allVertices = vertexSet $ _migration_graph m
145 visitFrom start = case Map.lookup start graph of
125 visitFrom start = case Map.lookup start graph of
146− Nothing -> throwM $ GraphInternalError $ "Vertex not found: " <> start
126+ Nothing -> throwError $ GraphInternalError $ "Vertex not found: " <> start
147 Just adjs -> case (Set.toList adjs) of
127 Just adjs -> case (Set.toList adjs) of
148 [] -> pure (start, [start])
128 [] -> pure (start, [start])
149 [adj] -> fmap (start :) <$> visitFrom adj
129 [adj] -> fmap (start :) <$> visitFrom adj
150− _ -> throwM $ NonLinearGraph_MultipleAdjacents start
130+ _ -> throwError $ NonLinearGraph_MultipleAdjacents start
151 (lastVertex, visitedVertices) <- visitFrom firstVertex
131 (lastVertex, visitedVertices) <- visitFrom firstVertex
152 let unvisitedVertices = Set.difference allVertices $ Set.fromList visitedVertices
132 let unvisitedVertices = Set.difference allVertices $ Set.fromList visitedVertices
153 if Set.null unvisitedVertices
133 if Set.null unvisitedVertices
154 then pure lastVertex
134 then pure lastVertex
155− else throwM $ NonLinearGraph_NotConnected unvisitedVertices
135+ else throwError $ NonLinearGraph_NotConnected unvisitedVertices
157-- | Find the concatenated actions between two vertices
137-- | Find the concatenated actions between two vertices
@@ -161,7 +141,7 @@ ensureGraphLinearity m = do
162-- Complexity: O(V+E)
142-- Complexity: O(V+E)
163findPathAction
143findPathAction
164− :: (MonadThrow m, Action action, Monoid action, Ord action)
144+ :: (MonadError MigrationError m, Action action, Monoid action, Ord action)
165 => Migration action -> Hash -> Hash -> m (Maybe action)
145 => Migration action -> Hash -> Hash -> m (Maybe action)
166findPathAction m start end = flip evalStateT Map.empty $ do
146findPathAction m start end = flip evalStateT Map.empty $ do
167 graph <- fmap adjacencyMap $ getDag $ _migration_graph m
147 graph <- fmap adjacencyMap $ getDag $ _migration_graph m
@@ -188,7 +168,7 @@ findPathAction m start end = flip evalStateT Map.empty $ do
188 case uniqs actions of
168 case uniqs actions of
189 [] -> pure Nothing
169 [] -> pure Nothing
190 [v] -> pure $ Just v -- Exactly one monoidal value; accept.
170 [v] -> pure $ Just v -- Exactly one monoidal value; accept.
191− _ -> throwM $ NonEquivalentPaths start' end
171+ _ -> throwError $ NonEquivalentPaths start' end
192 Nothing ->
172 Nothing ->
193 pure Nothing
173 pure Nothing
194 uniqs = Set.toList . Set.fromList
174 uniqs = Set.toList . Set.fromList
@@ -198,19 +178,19 @@ hasVertex :: Hash -> Migration action -> Bool
198hasVertex h (Migration g _) = Set.member h $ vertexSet g
178hasVertex h (Migration g _) = Set.member h $ vertexSet g
200-- | Get the action for the given edge (which must exist)
180-- | Get the action for the given edge (which must exist)
201−getAction :: MonadThrow m => Migration action -> (Hash, Hash) -> m action
181+getAction :: MonadError MigrationError m => Migration action -> (Hash, Hash) -> m action
202getAction (Migration _ h) e = case Map.lookup e h of
182getAction (Migration _ h) e = case Map.lookup e h of
203 Just v -> pure v
183 Just v -> pure v
204− Nothing -> throwM $ MissingEdgeInActionMap e
184+ Nothing -> throwError $ MissingEdgeInActionMap e
206-- | Get the last vertex of the given DAG. Nothing if empty graph.
186-- | Get the last vertex of the given DAG. Nothing if empty graph.
208-- Assumes that the graph is fully connected.
188-- Assumes that the graph is fully connected.
209−getLast :: (MonadThrow m, Ord a, Eq a) => AdjacencyMap a -> m a
189+getLast :: (MonadError MigrationError m, Ord a, Eq a) => AdjacencyMap a -> m a
210getLast g = lastVertices >>= \case
190getLast g = lastVertices >>= \case
211− [] -> throwM NoTerminalVertex
191+ [] -> throwError NoTerminalVertex
212 [x] -> pure x
192 [x] -> pure x
213− _ -> throwM MultipleTerminalVertices
193+ _ -> throwError MultipleTerminalVertices
215 lastVertices
195 lastVertices
@@ -220,7 +200,7 @@ getLast g = lastVertices >>= \case
220 =<< getDag g
200 =<< getDag g
222-- | Get the first vertex of the given DAG. Nothing if empty graph.
202-- | Get the first vertex of the given DAG. Nothing if empty graph.
223−getFirst :: (MonadThrow m, Ord a, Eq a) => AdjacencyMap a -> m a
203+getFirst :: (MonadError MigrationError m, Ord a, Eq a) => AdjacencyMap a -> m a
224getFirst = getLast . transpose
204getFirst = getLast . transpose
226-- | Write an edge directly to the filesystem.
206-- | Write an edge directly to the filesystem.