@@ -7,13 +7,11 @@ module Elm.Install
8import Control.Monad (filterM, forM, msum, void)
8import Control.Monad (filterM, forM, msum, void)
9import Control.Monad.Except (catchError, lift, liftIO)
9import Control.Monad.Except (catchError, lift, liftIO)
10−import Data.Map (Map, (!))
10+import Data.Map ((!))
11import qualified Data.Map as Map
11import qualified Data.Map as Map
12import qualified Data.Map.Merge.Lazy as Map
12import qualified Data.Map.Merge.Lazy as Map
13−import System.IO (stdout)
15import qualified Elm.Package as Pkg
14import qualified Elm.Package as Pkg
16−import Elm.Package (Name, Version)
18import qualified Deps.Cache as Cache
16import qualified Deps.Cache as Cache
19import Deps.Explorer (Explorer)
17import Deps.Explorer (Explorer)
@@ -35,57 +33,133 @@ import qualified Reporting.Task as Task
38−install :: Name -> Task.Task ()
36+install :: Pkg.Name -> Task.Task ()
39install pkg =
37install pkg =
40 do (root, oldProject) <- Root.unsafeGet
38 do (root, oldProject) <- Root.unsafeGet
41− makePlan pkg oldProject $ \newProject ->
44− do liftIO $ Project.write root newProject
45− void $ Verify.verify root newProject
47− revert err =
40+ case oldProject of
48− do liftIO $ Project.write root oldProject
41+ Project.App info ->
49− Task.throw err
42+ do changes <- makeAppPlan pkg info
50− in
43+ attemptElmJsonChange root oldProject Pkg.versionToString changes
51− upgrade `catchError` revert
46+ do changes <- makePkgPlan pkg info
47+ attemptElmJsonChange root oldProject Con.toString changes
50+attemptElmJsonChange :: FilePath -> Project.Project -> (a -> String) -> Changes a -> Task.Task ()
51+attemptElmJsonChange root oldProject toString changes =
53+ attempt newProject question =
54+ do approved <- Task.getApproval question
56+ then upgrade newProject `catchError` revert
57+ else liftIO $ putStrLn "Okay, I did not change anything!"
60+ do liftIO $ Project.write root newProject
61+ void $ Verify.verify root newProject
64+ do liftIO $ Project.write root oldProject
69+ do liftIO $ putStrLn "It is already installed!"
71+ PromoteTest newProject ->
72+ attempt newProject $ D.vcat $
73+ [ "I found it in your elm.json file!"
74+ , "In \"test-dependencies\" though."
75+ , "Should I move it into \"dependencies\" for more general use? [Y/n]: "
78+ PromoteTrans newProject ->
79+ attempt newProject $ D.vcat $
80+ [ "I found it in your elm.json file!"
81+ , "In \"transitive-dependencies\" though."
82+ , "Should I move it into \"dependencies\" for more general use? [Y/n]: "
85+ Changes changeDict newProject ->
87+ widths = Map.foldrWithKey (widen toString) (Widths 0 0 0) changeDict
88+ changeDocs = Map.foldrWithKey (addChange toString widths) (Docs [] [] []) changeDict
90+ attempt newProject $ D.vcat $
92+ , viewChangeDocs changeDocs
94+ , "Would you like me to update your elm.json accordingly? [Y/n]: "
55−-- MAKE PLAN
99+-- MAKE PLANS
58−makePlan :: Name -> Project.Project -> (Project.Project -> Task.Task ()) -> Task.Task ()
102+data Changes vsn
59−makePlan pkg project attemptInstall =
103+ = AlreadyInstalled
60− case project of
104+ | PromoteTest Project.Project
105+ | PromoteTrans Project.Project
106+ | Changes (Map.Map Pkg.Name (Change vsn)) Project.Project
109+makeAppPlan :: Pkg.Name -> Project.AppInfo -> Task.Task (Changes Pkg.Version)
61− Project.App info@(Project.AppInfo elm srcDirs deps test trans) ->
110+makeAppPlan pkg info@(Project.AppInfo elm srcDirs deps test trans) =
62− do registry <- Cache.optionalUpdate
111+ if Map.member pkg deps then
63− changes <- addToApp registry pkg info
112+ return AlreadyInstalled
65− let news = Map.mapMaybe keepNew changes
114+ case Map.lookup pkg test of
66− let newDeps = addNews (Just pkg) news deps
115+ Just vsn ->
67− let newTest = addNews Nothing news test
116+ return $ PromoteTest $ Project.App $
68− let newTrans = Map.union (Map.difference news (Map.union newDeps newTest)) trans
117+ Project.AppInfo elm srcDirs (Map.insert pkg vsn deps) (Map.delete pkg test) trans
69− let newInfo = Project.AppInfo elm srcDirs newDeps newTest newTrans
70− let newProject = Project.App newInfo
72− withApproval Pkg.versionToString changes (attemptInstall newProject)
119+ Nothing ->
120+ case Map.lookup pkg trans of
122+ return $ PromoteTrans $ Project.App $
123+ Project.AppInfo elm srcDirs (Map.insert pkg vsn deps) test (Map.delete pkg trans)
74− Project.Pkg info@(Project.PkgInfo _ _ _ _ _ deps test _) ->
125+ Nothing ->
75− do registry <- Cache.optionalUpdate
126+ do registry <- Cache.optionalUpdate
76− changes <- addToPkg registry pkg info
127+ changes <- addToApp registry pkg info
78− let news = Map.mapMaybe keepNew changes
129+ let news = Map.mapMaybe keepNew changes
79− let newProject = Project.Pkg $
81− { Project._pkg_deps = addNews (Just pkg) news deps
130+ let newDeps = addNews (Just pkg) news deps
82− , Project._pkg_test_deps = addNews Nothing news test
131+ let newTest = addNews Nothing news test
83− }
132+ let newTrans = Map.union (Map.difference news (Map.union newDeps newTest)) trans
85− withApproval (show . Con.toString) changes (attemptInstall newProject)
134+ return $ Changes changes $ Project.App $
135+ Project.AppInfo elm srcDirs newDeps newTest newTrans
138+makePkgPlan :: Pkg.Name -> Project.PkgInfo -> Task.Task (Changes Constraint)
139+makePkgPlan pkg info@(Project.PkgInfo _ _ _ _ _ deps test _) =
140+ if Map.member pkg deps then
141+ return AlreadyInstalled
143+ case Map.lookup pkg test of
145+ return $ PromoteTest $ Project.Pkg $
147+ { Project._pkg_deps = Map.insert pkg con deps
148+ , Project._pkg_test_deps = Map.delete pkg test
152+ do registry <- Cache.optionalUpdate
153+ changes <- addToPkg registry pkg info
154+ let news = Map.mapMaybe keepNew changes
155+ return $ Changes changes $ Project.Pkg $
157+ { Project._pkg_deps = addNews (Just pkg) news deps
158+ , Project._pkg_test_deps = addNews Nothing news test
88−addNews :: Maybe Name -> Map Name a -> Map Name a -> Map Name a
162+addNews :: Maybe Pkg.Name -> Map.Map Pkg.Name a -> Map.Map Pkg.Name a -> Map.Map Pkg.Name a
89addNews pkg new old =
163addNews pkg new old =
90 Map.merge
164 Map.merge
91 Map.preserveMissing
165 Map.preserveMissing
@@ -105,7 +179,7 @@ data Change a
105 | Remove a
179 | Remove a
108−detectChanges :: (Eq a) => Map Name a -> Map Name a -> Map Name (Change a)
182+detectChanges :: (Eq a) => Map.Map Pkg.Name a -> Map.Map Pkg.Name a -> Map.Map Pkg.Name (Change a)
109detectChanges old new =
183detectChanges old new =
110 Map.merge
184 Map.merge
111 (Map.mapMissing (\_ v -> Remove v))
185 (Map.mapMissing (\_ v -> Remove v))
@@ -140,7 +214,7 @@ keepNew change =
140-- ADD TO APP
214-- ADD TO APP
143−addToApp :: Cache.PackageRegistry -> Name -> Project.AppInfo -> Task.Task (Map Name (Change Version))
217+addToApp :: Cache.PackageRegistry -> Pkg.Name -> Project.AppInfo -> Task.Task (Map.Map Pkg.Name (Change Pkg.Version))
144addToApp registry pkg info@(Project.AppInfo _ _ deps tests trans) =
218addToApp registry pkg info@(Project.AppInfo _ _ deps tests trans) =
145 Explorer.run registry $
219 Explorer.run registry $
146 do Explorer.exists pkg
220 do Explorer.exists pkg
@@ -155,7 +229,7 @@ addToApp registry pkg info@(Project.AppInfo _ _ deps tests trans) =
155 lift $ Task.throw (Exit.Install (E.NoSolution badNames))
229 lift $ Task.throw (Exit.Install (E.NoSolution badNames))
158−addToAppHelp :: Name -> Project.AppInfo -> Solver.Solver (Map Name Version)
232+addToAppHelp :: Pkg.Name -> Project.AppInfo -> Solver.Solver (Map.Map Pkg.Name Pkg.Version)
159addToAppHelp pkg (Project.AppInfo _ _ deps tests trans) =
233addToAppHelp pkg (Project.AppInfo _ _ deps tests trans) =
161 directs =
235 directs =
@@ -180,7 +254,7 @@ addToAppHelp pkg (Project.AppInfo _ _ deps tests trans) =
180-- ADD TO PKG
254-- ADD TO PKG
183−addToPkg :: Cache.PackageRegistry -> Name -> Project.PkgInfo -> Task.Task (Map Name (Change Constraint))
257+addToPkg :: Cache.PackageRegistry -> Pkg.Name -> Project.PkgInfo -> Task.Task (Map.Map Pkg.Name (Change Constraint))
184addToPkg registry pkg info@(Project.PkgInfo _ _ _ _ _ deps tests _) =
258addToPkg registry pkg info@(Project.PkgInfo _ _ _ _ _ deps tests _) =
185 Explorer.run registry $
259 Explorer.run registry $
186 do Explorer.exists pkg
260 do Explorer.exists pkg
@@ -196,7 +270,7 @@ addToPkg registry pkg info@(Project.PkgInfo _ _ _ _ _ deps tests _) =
196 lift $ Task.throw (Exit.Install (E.NoSolution badNames))
270 lift $ Task.throw (Exit.Install (E.NoSolution badNames))
199−addToPkgHelp :: Name -> Project.PkgInfo -> Solver.Solver (Map Name Constraint)
273+addToPkgHelp :: Pkg.Name -> Project.PkgInfo -> Solver.Solver (Map.Map Pkg.Name Constraint)
200addToPkgHelp pkg (Project.PkgInfo _ _ _ _ _ deps tests _) =
274addToPkgHelp pkg (Project.PkgInfo _ _ _ _ _ deps tests _) =
201 do let directs = Map.union deps tests
275 do let directs = Map.union deps tests
202 let newCons = Map.insert pkg Con.anything directs
276 let newCons = Map.insert pkg Con.anything directs
@@ -209,7 +283,7 @@ addToPkgHelp pkg (Project.PkgInfo _ _ _ _ _ deps tests _) =
209-- FAILURE HINTS
283-- FAILURE HINTS
212−isBadElm :: Name -> Explorer Bool
286+isBadElm :: Pkg.Name -> Explorer Bool
213isBadElm name =
287isBadElm name =
214 do versions <- Explorer.getVersions name
288 do versions <- Explorer.getVersions name
@@ -220,36 +294,6 @@ isBadElm name =
226−withApproval :: (a -> String) -> Map Name (Change a) -> Task.Task () -> Task.Task ()
227−withApproval toString changes attemptInstall =
230− Map.foldrWithKey (widen toString) (Widths 0 0 0) changes
233− Map.foldrWithKey (addChange toString widths) (Docs [] [] []) changes
237− do liftIO $ putStrLn "Already installed. No changes necessary!"
239− Docs inserts [] [] ->
240− do liftIO $ putStrLn "Adding the following dependencies:\n"
241− liftIO $ D.toAnsi stdout $ D.indent 2 (D.vcat (reverse inserts))
242− liftIO $ putStrLn "\n"
246− do liftIO $ putStrLn "Here is my plan:"
247− liftIO $ D.toAnsi stdout $ viewChangeDocs changeDocs
248− liftIO $ putStrLn "\nDo you approve? [Y/n]: "
249− Task.withApproval attemptInstall
253-- VIEW CHANGE DOCS
297-- VIEW CHANGE DOCS
@@ -285,7 +329,7 @@ viewNonZero title entries =
285-- VIEW CHANGE
329-- VIEW CHANGE
288−addChange :: (a -> String) -> Widths -> Name -> Change a -> ChangeDocs -> ChangeDocs
332+addChange :: (a -> String) -> Widths -> Pkg.Name -> Change a -> ChangeDocs -> ChangeDocs
289addChange toString widths name change (Docs inserts changes removes) =
333addChange toString widths name change (Docs inserts changes removes) =
290 case change of
334 case change of
291 Insert new ->
335 Insert new ->
@@ -298,12 +342,12 @@ addChange toString widths name change (Docs inserts changes removes) =
298 Docs inserts changes (viewRemove toString widths name old : removes)
342 Docs inserts changes (viewRemove toString widths name old : removes)
301−viewInsert :: (a -> String) -> Widths -> Name -> a -> D.Doc
345+viewInsert :: (a -> String) -> Widths -> Pkg.Name -> a -> D.Doc
302viewInsert toString (Widths nameWidth leftWidth _) name new =
346viewInsert toString (Widths nameWidth leftWidth _) name new =
303 viewName nameWidth name <+> pad leftWidth (toString new)
347 viewName nameWidth name <+> pad leftWidth (toString new)
306−viewChange :: (a -> String) -> Widths -> Name -> a -> a -> D.Doc
350+viewChange :: (a -> String) -> Widths -> Pkg.Name -> a -> a -> D.Doc
307viewChange toString (Widths nameWidth leftWidth rightWidth) name old new =
351viewChange toString (Widths nameWidth leftWidth rightWidth) name old new =
309 [ viewName nameWidth name
353 [ viewName nameWidth name
@@ -313,12 +357,12 @@ viewChange toString (Widths nameWidth leftWidth rightWidth) name old new =
316−viewRemove :: (a -> String) -> Widths -> Name -> a -> D.Doc
360+viewRemove :: (a -> String) -> Widths -> Pkg.Name -> a -> D.Doc
317viewRemove toString (Widths nameWidth leftWidth _) name old =
361viewRemove toString (Widths nameWidth leftWidth _) name old =
318 viewName nameWidth name <+> pad leftWidth (toString old)
362 viewName nameWidth name <+> pad leftWidth (toString old)
321−viewName :: Int -> Name -> D.Doc
365+viewName :: Int -> Pkg.Name -> D.Doc
322viewName width name =
366viewName width name =
323 D.fill (width + 3) (D.fromText (Pkg.toText name))
367 D.fill (width + 3) (D.fromText (Pkg.toText name))
@@ -340,7 +384,7 @@ data Widths =
343−widen :: (a -> String) -> Name -> Change a -> Widths -> Widths
387+widen :: (a -> String) -> Pkg.Name -> Change a -> Widths -> Widths
344widen toString pkg change (Widths name left right) =
388widen toString pkg change (Widths name left right) =
346 toLength a =
390 toLength a =