{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} -- | Types and functions related to Stack's @list@ command. module Stack.List ( listCmd , listPackages ) where import qualified RIO.ByteString.Lazy as Lazy import qualified RIO.Map as Map import RIO.Process ( HasProcessContext ) import Stack.Config ( getRawSnapshot ) import Stack.Prelude import Stack.Runners ( ShouldReexec (..), withConfig ) import Stack.SourceMap ( globalsFromHints ) import Stack.Types.Runner ( Runner ) -- | Type representing exceptions thrown by functions exported by the -- "Stack.List" module. newtype ListPrettyException = CouldNotParsePackageSelectors [StyleDoc] deriving (Show, Typeable) instance Pretty ListPrettyException where pretty (CouldNotParsePackageSelectors errs) = "[S-4926]" <> line <> bulletedList errs instance Exception ListPrettyException -- | Function underlying the @stack list@ command. List packages. listCmd :: [String] -> RIO Runner () listCmd names = withConfig NoReexec $ do mSnapshot <- getRawSnapshot let mWc = rsCompiler <$> mSnapshot mGlobals <- mapM globalsFromHints mWc listPackages mSnapshot mGlobals names -- | Intended to work for the command line command. listPackages :: forall env. (HasPantryConfig env, HasProcessContext env, HasTerm env) => Maybe RawSnapshot -- ^ When looking up by name, take from this build plan. -> Maybe (Map PackageName Version) -- ^ Global hints for snapshot wanted compiler. -> [String] -- ^ Names or identifiers. -> RIO env () listPackages mSnapshot mGlobals input = do let (errs1, names) = case mSnapshot of Just snapshot | null input -> ([], Map.keys (rsPackages snapshot)) _ -> partitionEithers $ map parse input (errs2, locs) <- partitionEithers <$> traverse toLoc names case errs1 ++ errs2 of [] -> pure () errs -> prettyThrowM $ CouldNotParsePackageSelectors errs mapM_ (Lazy.putStrLn . fromPackageId) locs where toLoc | Just snapshot <- mSnapshot = toLocSnapshot snapshot | otherwise = toLocNoSnapshot toLocNoSnapshot :: PackageName -> RIO env (Either StyleDoc PackageIdentifier) toLocNoSnapshot name = do mloc1 <- getLatestHackageLocation YesRequireHackageIndex name UsePreferredVersions mloc <- case mloc1 of Just _ -> pure mloc1 Nothing -> do updated <- updateHackageIndex $ Just $ "Could not find package " <> fromPackageName name <> ", updating" case updated of UpdateOccurred -> getLatestHackageLocation YesRequireHackageIndex name UsePreferredVersions NoUpdateOccurred -> pure Nothing case mloc of Nothing -> do candidates <- getHackageTypoCorrections name pure $ Left $ fillSep [ flow "Could not find package" , style Current (fromPackageName name) , flow "on Hackage." , if null candidates then mempty else fillSep $ flow "Perhaps you meant one of:" : mkNarrativeList (Just Good) False (map fromPackageName candidates :: [StyleDoc]) ] Just loc -> pure $ Right (packageLocationIdent loc) toLocSnapshot :: RawSnapshot -> PackageName -> RIO env (Either StyleDoc PackageIdentifier) toLocSnapshot snapshot name = case Map.lookup name (rsPackages snapshot) of Nothing -> case Map.lookup name =<< mGlobals of Nothing -> pure $ Left $ fillSep [ flow "Package does not appear in snapshot (directly or \ \indirectly):" , style Current (fromPackageName name) <> "." ] Just version -> pure $ Right $ PackageIdentifier name version Just sp -> do loc <- cplComplete <$> completePackageLocation (rspLocation sp) pure $ Right (packageLocationIdent loc) parse s = case parsePackageName s of Just x -> Right x Nothing -> Left $ fillSep [ flow "Could not parse as package name or identifier:" , style Current (fromString s) <> "." ]