1{-# LANGUAGE OverloadedStrings #-}
10import qualified Data.Map as Map
11import qualified Data.Map.Merge.Strict as Map
13import qualified Deps.Solver as Solver
14import qualified Deps.Registry as Registry
15import qualified Elm.Constraint as C
16import qualified Elm.Details as Details
17import qualified Elm.Package as Pkg
18import qualified Elm.Outline as Outline
19import qualified Elm.Version as V
20import qualified Reporting
21import Reporting.Doc ((<>), (<+>))
22import qualified Reporting.Doc as D
23import qualified Reporting.Exit as Exit
24import qualified Reporting.Task as Task
37run :: Args -> () -> IO ()
39 Reporting.attempt Exit.installToReport $
40 do maybeRoot <- Stuff.findRoot
43 return (Left Exit.InstallNoOutline)
48 do elmHome <- Stuff.getElmHome
49 return (Left (Exit.InstallNoArgs elmHome))
53 do env <- Task.eio Exit.InstallBadRegistry $ Solver.initEnv
54 oldOutline <- Task.eio Exit.InstallBadOutline $ Outline.read root
56 Outline.App outline ->
57 do changes <- makeAppPlan env pkg outline
58 attemptChanges root env pkg oldOutline V.toChars changes
60 Outline.Pkg outline ->
61 do changes <- makePkgPlan env pkg outline
62 attemptChanges root env pkg oldOutline C.toChars changes
71 | PromoteTest Outline.Outline
72 | PromoteIndirect Outline.Outline
73 | Changes (Map.Map Pkg.Name (Change vsn)) Outline.Outline
76type Task = Task.Task Exit.Install
79attemptChanges :: FilePath -> Solver.Env -> Pkg.Name -> Outline.Outline -> (a -> String) -> Changes a -> Task ()
80attemptChanges root env pkg oldOutline toChars changes =
83 Task.io $ putStrLn "It is already installed!"
85 PromoteIndirect newOutline ->
86 attemptChangesHelp root env pkg oldOutline newOutline $
89 ["I","found","it","in","your","elm.json","file,"
90 ,"but","in","the",D.dullyellow "\"indirect\"","dependencies."
93 ["Should","I","move","it","into",D.green "\"direct\""
94 ,"dependencies","for","more","general","use?","[Y/n]: "
98 PromoteTest newOutline ->
99 attemptChangesHelp root env pkg oldOutline newOutline $
102 ["I","found","it","in","your","elm.json","file,"
103 ,"but","in","the",D.dullyellow "\"test-dependencies\"","field."
106 ["Should","I","move","it","into",D.green "\"dependencies\""
107 ,"for","more","general","use?","[Y/n]: "
111 Changes changeDict newOutline ->
113 widths = Map.foldrWithKey (widen toChars) (Widths 0 0 0) changeDict
114 changeDocs = Map.foldrWithKey (addChange toChars widths) (Docs [] [] []) changeDict
116 attemptChangesHelp root env pkg oldOutline newOutline $ D.vcat $
118 , viewChangeDocs changeDocs
120 , "Would you like me to update your elm.json accordingly? [Y/n]: "
124attemptChangesHelp :: FilePath -> Solver.Env -> Pkg.Name -> Outline.Outline -> Outline.Outline -> D.Doc -> Task ()
125attemptChangesHelp root env pkg oldOutline newOutline question =
126 Task.eio (\_ -> Exit.InstallHasBadDetails pkg (Outline.encode newOutline)) $
127 do approved <- Reporting.ask question
130 do Outline.write root newOutline
131 result <- Details.verifyInstall root env newOutline
134 do Outline.write root oldOutline
138 do putStrLn "Success!"
141 do putStrLn "Okay, I did not change anything!"
149makeAppPlan :: Solver.Env -> Pkg.Name -> Outline.AppOutline -> Task (Changes V.Version)
150makeAppPlan (Solver.Env cache _ connection registry) pkg outline@(Outline.AppOutline _ _ direct indirect testDirect testIndirect) =
151 if Map.member pkg direct then
152 return AlreadyInstalled
155 -- is it already indirect?
156 case Map.lookup pkg indirect of
158 return $ PromoteIndirect $ Outline.App $
160 { Outline._app_deps_direct = Map.insert pkg vsn direct
161 , Outline._app_deps_indirect = Map.delete pkg indirect
165 -- is it already a test dependency?
166 case Map.lookup pkg testDirect of
168 return $ PromoteTest $ Outline.App $
170 { Outline._app_deps_direct = Map.insert pkg vsn direct
171 , Outline._app_test_direct = Map.delete pkg testDirect
175 -- is it already an indirect test dependency?
176 case Map.lookup pkg testIndirect of
178 return $ PromoteTest $ Outline.App $
180 { Outline._app_deps_direct = Map.insert pkg vsn direct
181 , Outline._app_test_indirect = Map.delete pkg testIndirect
185 -- finally try to add it from scratch
186 case Registry.getVersions' pkg registry of
189 Solver.Online _ -> Task.throw (Exit.InstallUnknownPackageOnline pkg suggestions)
190 Solver.Offline -> Task.throw (Exit.InstallUnknownPackageOffline pkg suggestions)
193 do result <- Task.io $ Solver.addToApp cache connection registry pkg outline
195 Solver.Ok (Solver.AppSolution old new app) ->
196 return (Changes (detectChanges old new) (Outline.App app))
199 Task.throw Exit.InstallNoOnlineSolution
201 Solver.NoOfflineSolution ->
202 Task.throw Exit.InstallNoOfflineSolution
205 Task.throw (Exit.InstallHadSolverTrouble exit)
212makePkgPlan :: Solver.Env -> Pkg.Name -> Outline.PkgOutline -> Task (Changes C.Constraint)
213makePkgPlan (Solver.Env cache _ connection registry) pkg outline@(Outline.PkgOutline _ _ _ _ _ deps test _) =
214 if Map.member pkg deps then
215 return AlreadyInstalled
217 -- is already in test dependencies?
218 case Map.lookup pkg test of
220 return $ PromoteTest $ Outline.Pkg $
222 { Outline._pkg_deps = Map.insert pkg con deps
223 , Outline._pkg_test_deps = Map.delete pkg test
227 -- try to add a new dependency
228 case Registry.getVersions' pkg registry of
231 Solver.Online _ -> Task.throw (Exit.InstallUnknownPackageOnline pkg suggestions)
232 Solver.Offline -> Task.throw (Exit.InstallUnknownPackageOffline pkg suggestions)
234 Right (Registry.KnownVersions _ _) ->
235 do let old = Map.union deps test
236 let cons = Map.insert pkg C.anything old
237 result <- Task.io $ Solver.verify cache connection registry cons
239 Solver.Ok solution ->
241 (Solver.Details vsn _) = solution ! pkg
243 con = C.untilNextMajor vsn
244 new = Map.insert pkg con old
245 changes = detectChanges old new
246 news = Map.mapMaybe keepNew changes
248 return $ Changes changes $ Outline.Pkg $
250 { Outline._pkg_deps = addNews (Just pkg) news deps
251 , Outline._pkg_test_deps = addNews Nothing news test
255 Task.throw Exit.InstallNoOnlineSolution
257 Solver.NoOfflineSolution ->
258 Task.throw Exit.InstallNoOfflineSolution
261 Task.throw (Exit.InstallHadSolverTrouble exit)
264addNews :: Maybe Pkg.Name -> Map.Map Pkg.Name C.Constraint -> Map.Map Pkg.Name C.Constraint -> Map.Map Pkg.Name C.Constraint
268 (Map.mapMaybeMissing (\k c -> if Just k == pkg then Just c else Nothing))
269 (Map.zipWithMatched (\_ _ n -> n))
284detectChanges :: (Eq a) => Map.Map Pkg.Name a -> Map.Map Pkg.Name a -> Map.Map Pkg.Name (Change a)
285detectChanges old new =
287 (Map.mapMissing (\_ v -> Remove v))
288 (Map.mapMissing (\_ v -> Insert v))
289 (Map.zipWithMaybeMatched keepChange)
294keepChange :: (Eq v) => k -> v -> v -> Maybe (Change v)
295keepChange _ old new =
299 Just (Change old new)
302keepNew :: Change a -> Maybe a
321 { _doc_inserts :: [D.Doc]
322 , _doc_changes :: [D.Doc]
323 , _doc_removes :: [D.Doc]
327viewChangeDocs :: ChangeDocs -> D.Doc
328viewChangeDocs (Docs inserts changes removes) =
329 D.indent 2 $ D.vcat $ concat $
330 [ viewNonZero "Add:" inserts
331 , viewNonZero "Change:" changes
332 , viewNonZero "Remove:" removes
336viewNonZero :: String -> [D.Doc] -> [D.Doc]
337viewNonZero title entries =
343 , D.indent 2 (D.vcat entries)
351addChange :: (a -> String) -> Widths -> Pkg.Name -> Change a -> ChangeDocs -> ChangeDocs
352addChange toChars widths name change (Docs inserts changes removes) =
355 Docs (viewInsert toChars widths name new : inserts) changes removes
358 Docs inserts (viewChange toChars widths name old new : changes) removes
361 Docs inserts changes (viewRemove toChars widths name old : removes)
364viewInsert :: (a -> String) -> Widths -> Pkg.Name -> a -> D.Doc
365viewInsert toChars (Widths nameWidth leftWidth _) name new =
366 viewName nameWidth name <+> pad leftWidth (toChars new)
369viewChange :: (a -> String) -> Widths -> Pkg.Name -> a -> a -> D.Doc
370viewChange toChars (Widths nameWidth leftWidth rightWidth) name old new =
372 [ viewName nameWidth name
373 , pad leftWidth (toChars old)
375 , pad rightWidth (toChars new)
379viewRemove :: (a -> String) -> Widths -> Pkg.Name -> a -> D.Doc
380viewRemove toChars (Widths nameWidth leftWidth _) name old =
381 viewName nameWidth name <+> pad leftWidth (toChars old)
384viewName :: Int -> Pkg.Name -> D.Doc
386 D.fill (width + 3) (D.fromPackage name)
389pad :: Int -> String -> D.Doc
391 D.fromChars (replicate (width - length string) ' ') <> D.fromChars string
406widen :: (a -> String) -> Pkg.Name -> Change a -> Widths -> Widths
407widen toChars pkg change (Widths name left right) =
413 max name (length (Pkg.toChars pkg))
417 Widths newName (max left (toLength new)) right
420 Widths newName (max left (toLength old)) (max right (toLength new))
423 Widths newName (max left (toLength old)) right
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list