@@ -1,88 +1,151 @@
1+{-# LANGUAGE OverloadedStrings, Rank2Types #-}
1module Deps.Solver
2module Deps.Solver
9−import Control.Monad (foldM, guard, mzero, msum)
11+import Control.Monad (foldM)
10−import Control.Monad.Logic (LogicT, runLogicT, lift)
11−import qualified Data.List as List
12import qualified Data.Map as Map
12import qualified Data.Map as Map
13−import Data.Map (Map)
13+import qualified System.Directory as Dir
15−import Deps.Explorer (Explorer)
15+import qualified Deps.Registry as Registry
16−import qualified Deps.Explorer as Explorer
16+import qualified Deps.Website as Website
17import qualified Elm.Constraint as C
17import qualified Elm.Constraint as C
18import qualified Elm.Package as Pkg
18import qualified Elm.Package as Pkg
19+import qualified Elm.Outline as Outline
19import qualified Elm.Version as V
20import qualified Elm.Version as V
23+import qualified Json.Decode as D
24+import qualified Stuff
26−solve :: Map Pkg.Name C.Constraint -> Solver (Map Pkg.Name V.Version)
31+newtype Solver a =
27−solve cons =
32+ Solver
28− mkSolver (State Map.empty cons)
33+ (
36+ -> (State -> a -> (State -> IO b) -> IO b)
31−type Solver a =
42+data State =
32− LogicT Explorer a
43+ State
44+ { _cache :: Stuff.PackageCache
45+ , _internet :: Connection
46+ , _registry :: Registry.Registry
47+ , _constraints :: Map.Map (Pkg.Name, V.Version) Constraints
48+ , _problems :: [Problem]
35−run :: Solver a -> Explorer (Maybe a)
52+data Constraints =
36−run solver =
53+ Constraints
37− runLogicT solver (const . return . Just) (return Nothing)
54+ { _elm :: C.Constraint
55+ , _deps :: Map.Map Pkg.Name C.Constraint
60+ = Online Http.Manager
65+ = BadCacheData Pkg.Name V.Version
66+ | BadHttpData Pkg.Name V.Version
68+ | UnknownPackage [Pkg.Name]
69+ | ImpossibleConstraint Pkg.Name C.Constraint
70+ | UnavailableOffline Pkg.Name V.Version
78+ Details V.Version [Pkg.Name]
46− { _solution :: Map Pkg.Name V.Version
47− , _unsolved :: Map Pkg.Name C.Constraint
81+solve :: Stuff.PackageCache -> Connection -> Registry.Registry -> Map.Map Pkg.Name C.Constraint -> IO (Either [Problem] (Map.Map Pkg.Name Details))
82+solve cache connection registry constraints =
84+ (Solver solver) = exploreGoals (Goals constraints Map.empty)
86+ solver (State cache connection registry Map.empty [])
87+ (\(State _ _ _ cs _) vs _ -> return $ Right $ getDetails vs cs)
88+ (\(State _ _ _ _ ps) -> return $ Left ps)
91+getDetails :: Map.Map Pkg.Name V.Version -> Map.Map (Pkg.Name, V.Version) Constraints -> Map.Map Pkg.Name Details
92+getDetails solution constraints =
93+ Map.mapWithKey (getDetailsHelp constraints) solution
96+getDetailsHelp :: Map.Map (Pkg.Name, V.Version) Constraints -> Pkg.Name -> V.Version -> Details
97+getDetailsHelp constraints name vsn =
98+ case Map.lookup (name, vsn) constraints of
99+ Just (Constraints _ deps) ->
100+ Details vsn (Map.keys deps)
51−mkSolver :: State -> Solver (Map Pkg.Name V.Version)
52−mkSolver (State solution unsolved) =
53− case Map.minViewWithKey unsolved of
54 Nothing ->
102 Nothing ->
55− return solution
103+ error "compiler bug in Deps.Solver manifesting in getDetailsHelp"
57− Just ((name, constraint), otherUnsolved) ->
58− do allVersions <- lift $ Explorer.getVersions name
60− reverse $ List.sort $
61− filter (C.satisfies constraint) allVersions
63− let state1 = State solution otherUnsolved
107+-- EXPLORE GOALS
64− state2 <- msum (map (addVersion state1 name) versions)
112+ { _pending :: Map.Map Pkg.Name C.Constraint
68−addVersion :: State -> Pkg.Name -> V.Version -> Solver State
113+ , _solved :: Map.Map Pkg.Name V.Version
69−addVersion (State solution unsolved) name version =
114+ }
70− do (Explorer.Info elm cons) <-
115+
71− lift $ Explorer.getConstraints name version
116+
117+exploreGoals :: Goals -> Solver (Map.Map Pkg.Name V.Version)
118+exploreGoals (Goals pending solved) =
119+ case Map.minViewWithKey pending of
73− guard (C.goodElm elm)
123+ Just ((name, constraint), otherPending) ->
74− newUnsolved <- foldM (addConstraint solution) unsolved (Map.toList cons)
124+ do let goals1 = Goals otherPending solved
75− return (State (Map.insert name version solution) newUnsolved)
125+ let addVsn = addVersion goals1 name
126+ (v,vs) <- getRelevantVersions name constraint
127+ goals2 <- oneOf (addVsn v) (map addVsn vs)
131+addVersion :: Goals -> Pkg.Name -> V.Version -> Solver Goals
132+addVersion (Goals pending solved) name version =
133+ do (Constraints elm deps) <- getConstraints name version
136+ do newPending <- foldM (addConstraint solved) pending (Map.toList deps)
137+ return (Goals newPending (Map.insert name version solved))
78−addConstraint :: Map Pkg.Name V.Version -> Map Pkg.Name C.Constraint -> (Pkg.Name, C.Constraint) -> Solver (Map Pkg.Name C.Constraint)
142+addConstraint :: Map.Map Pkg.Name V.Version -> Map.Map Pkg.Name C.Constraint -> (Pkg.Name, C.Constraint) -> Solver (Map.Map Pkg.Name C.Constraint)
79−addConstraint solution unsolved (name, newConstraint) =
143+addConstraint solved unsolved (name, newConstraint) =
80− case Map.lookup name solution of
144+ case Map.lookup name solved of
81 Just version ->
145 Just version ->
82− if C.satisfies newConstraint version then
146+ if C.satisfies newConstraint version
83− return unsolved
147+ then return unsolved
84− else
148+ else deadend
87 Nothing ->
150 Nothing ->
88 case Map.lookup name unsolved of
151 case Map.lookup name unsolved of
@@ -92,11 +155,168 @@ addConstraint solution unsolved (name, newConstraint) =
92 Just oldConstraint ->
155 Just oldConstraint ->
93 case C.intersect oldConstraint newConstraint of
156 case C.intersect oldConstraint newConstraint of
94 Nothing ->
157 Nothing ->
97 Just mergedConstraint ->
160 Just mergedConstraint ->
98− if oldConstraint == mergedConstraint then
161+ if oldConstraint == mergedConstraint
99− return unsolved
162+ then return unsolved
101− return (Map.insert name mergedConstraint unsolved)
163+ else return (Map.insert name mergedConstraint unsolved)
167+-- GET RELEVANT VERSIONS
170+getRelevantVersions :: Pkg.Name -> C.Constraint -> Solver (V.Version, [V.Version])
171+getRelevantVersions name constraint =
172+ Solver $ \state@(State _ _ registry _ _) ok err ->
173+ case Registry.getVersions' name registry of
174+ Right (Registry.KnownVersions newest previous) ->
175+ case filter (C.satisfies constraint) (newest:previous) of
177+ err (addProblem (ImpossibleConstraint name constraint) state)
183+ err (addProblem (UnknownPackage suggestions) state)
190+getConstraints :: Pkg.Name -> V.Version -> Solver Constraints
191+getConstraints name version =
192+ Solver $ \state@(State cache connection registry cDict ps) ok err ->
193+ do let key = (name, version)
194+ case Map.lookup key cDict of
199+ do result <- getConstraintsHelp cache connection name version
202+ ok (State cache connection registry (Map.insert key cs cDict) ps) cs err
205+ err (State cache connection registry cDict (p:ps))
208+getConstraintsHelp :: Stuff.PackageCache -> Connection -> Pkg.Name -> V.Version -> IO (Either Problem Constraints)
209+getConstraintsHelp cache connection name version =
210+ do let path = Stuff.metadata cache name version "elm.json"
211+ outlineExists <- File.exists path
214+ do result <- D.fromFile constraintsDecoder path
222+ do srcExists <- Dir.doesDirectoryExist (Stuff.metadata cache name version "src")
224+ then return $ Right cs
225+ else return $ Left (UnavailableOffline name version)
229+ return $ Left (BadCacheData name version)
233+ return (Left (UnavailableOffline name version))
236+ Http.post manager (Website.metadata name version "elm.json") [] BadHttp $ \body ->
237+ case D.fromByteString constraintsDecoder body of
239+ do File.writeUtf8 path body
243+ return $ Left (BadHttpData name version)
246+constraintsDecoder :: D.Decoder () Constraints
248+ do outline <- D.mapError (const ()) Outline.decoder
250+ Outline.Pkg (Outline.PkgOutline _ _ _ _ _ deps _ elmConstraint) ->
251+ return (Constraints elmConstraint deps)
261+instance Functor Solver where
262+ fmap func (Solver solver) =
263+ Solver $ \state ok err ->
265+ okA stateA arg errA = ok stateA (func arg) errA
267+ solver state okA err
270+instance Applicative Solver where
272+ Solver $ \state ok err -> ok state a err
274+ (<*>) (Solver solverFunc) (Solver solverArg) =
275+ Solver $ \state ok err ->
277+ okF stateF func errF =
279+ okA stateA arg errA = ok stateA (func arg) errA
281+ solverArg stateF okA errF
283+ solverFunc state okF err
286+instance Monad Solver where
288+ Solver $ \state ok err -> ok state a err
290+ (>>=) (Solver solverA) callback =
291+ Solver $ \state ok err ->
295+ Solver solverB -> solverB stateA ok errA
297+ solverA state okA err
300+oneOf :: Solver a -> [Solver a] -> Solver a
301+oneOf solver@(Solver solverHead) solvers =
307+ Solver $ \state0 ok err ->
308+ solverHead state0 ok $ \state1 ->
310+ (Solver solverTail) = oneOf s ss
312+ solverTail state1 ok err
317+ Solver $ \state _ err -> err state
320+addProblem :: Problem -> State -> State
321+addProblem problem (State cache registry cDict maybeManager problems) =
322+ State cache registry cDict maybeManager (problem:problems)