packages feed

staversion 0.1.2.0 → 0.1.3.0

raw patch · 20 files changed

+1139/−185 lines, 20 filesdep +megaparsecdep ~QuickCheckdep ~basedep ~directoryPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: megaparsec

Dependency ranges changed: QuickCheck, base, directory

API changes (from Hackage documentation)

- Staversion.Internal.BuildPlan: _setDisambiguator :: BuildPlanManager -> Maybe Disambiguator -> IO ()
- Staversion.Internal.BuildPlan: instance Data.Aeson.Types.FromJSON.FromJSON Staversion.Internal.BuildPlan.BuildPlan
- Staversion.Internal.BuildPlan: instance GHC.Base.Monoid Staversion.Internal.BuildPlan.BuildPlan
- Staversion.Internal.BuildPlan: loadBuildPlanYAML :: FilePath -> IO (Either ErrorMsg BuildPlan)
- Staversion.Internal.Query: Result :: PackageSource -> Query -> Either ErrorMsg ResultVersions -> Result
- Staversion.Internal.Query: [resultFor] :: Result -> Query
- Staversion.Internal.Query: [resultIn] :: Result -> PackageSource
- Staversion.Internal.Query: [resultVersions] :: Result -> Either ErrorMsg ResultVersions
- Staversion.Internal.Query: data Result
- Staversion.Internal.Query: data ResultVersions
- Staversion.Internal.Query: instance GHC.Classes.Eq Staversion.Internal.Query.Result
- Staversion.Internal.Query: instance GHC.Classes.Eq Staversion.Internal.Query.ResultVersions
- Staversion.Internal.Query: instance GHC.Show.Show Staversion.Internal.Query.Result
- Staversion.Internal.Query: instance GHC.Show.Show Staversion.Internal.Query.ResultVersions
- Staversion.Internal.Query: resultVersionsFromList :: [(PackageName, Maybe Version)] -> ResultVersions
- Staversion.Internal.Query: resultVersionsToList :: ResultVersions -> [(PackageName, Maybe Version)]
+ Staversion.Internal.BuildPlan: _setLTSDisambiguator :: BuildPlanManager -> Word -> Word -> IO ()
+ Staversion.Internal.BuildPlan: buildPlanSource :: BuildPlan -> PackageSource
+ Staversion.Internal.BuildPlan: class HasVersions t
+ Staversion.Internal.BuildPlan: data BuildPlanMap
+ Staversion.Internal.BuildPlan: instance Data.Aeson.Types.FromJSON.FromJSON Staversion.Internal.BuildPlan.BuildPlanMap
+ Staversion.Internal.BuildPlan: instance GHC.Base.Monoid Staversion.Internal.BuildPlan.BuildPlanMap
+ Staversion.Internal.BuildPlan: instance Staversion.Internal.BuildPlan.HasVersions Staversion.Internal.BuildPlan.BuildPlan
+ Staversion.Internal.BuildPlan: instance Staversion.Internal.BuildPlan.HasVersions Staversion.Internal.BuildPlan.BuildPlanMap
+ Staversion.Internal.BuildPlan: loadBuildPlanMapYAML :: FilePath -> IO (Either ErrorMsg BuildPlanMap)
+ Staversion.Internal.BuildPlan.Stackage: formatExactResolverString :: ExactResolver -> Resolver
+ Staversion.Internal.Cabal: BuildDepends :: Target -> [PackageName] -> BuildDepends
+ Staversion.Internal.Cabal: TargetBenchmark :: Text -> Target
+ Staversion.Internal.Cabal: TargetExecutable :: Text -> Target
+ Staversion.Internal.Cabal: TargetLibrary :: Target
+ Staversion.Internal.Cabal: TargetTestSuite :: Text -> Target
+ Staversion.Internal.Cabal: [depsPackages] :: BuildDepends -> [PackageName]
+ Staversion.Internal.Cabal: [depsTarget] :: BuildDepends -> Target
+ Staversion.Internal.Cabal: data BuildDepends
+ Staversion.Internal.Cabal: data Target
+ Staversion.Internal.Cabal: instance GHC.Classes.Eq Staversion.Internal.Cabal.BuildDepends
+ Staversion.Internal.Cabal: instance GHC.Classes.Eq Staversion.Internal.Cabal.Target
+ Staversion.Internal.Cabal: instance GHC.Classes.Ord Staversion.Internal.Cabal.BuildDepends
+ Staversion.Internal.Cabal: instance GHC.Classes.Ord Staversion.Internal.Cabal.Target
+ Staversion.Internal.Cabal: instance GHC.Show.Show Staversion.Internal.Cabal.BuildDepends
+ Staversion.Internal.Cabal: instance GHC.Show.Show Staversion.Internal.Cabal.Target
+ Staversion.Internal.Cabal: loadCabalFile :: FilePath -> IO (Either ErrorMsg [BuildDepends])
+ Staversion.Internal.Exec: _processCommandWithCustomBuildPlanManager :: (BuildPlanManager -> IO BuildPlanManager) -> Command -> IO [Result]
+ Staversion.Internal.Exec: instance GHC.Classes.Eq Staversion.Internal.Exec.ResolvedQuery
+ Staversion.Internal.Exec: instance GHC.Classes.Ord Staversion.Internal.Exec.ResolvedQuery
+ Staversion.Internal.Exec: instance GHC.Show.Show Staversion.Internal.Exec.ResolvedQuery
+ Staversion.Internal.Query: QueryCabalFile :: FilePath -> Query
+ Staversion.Internal.Query: parseQuery :: String -> Query
+ Staversion.Internal.Result: CabalResultBody :: FilePath -> Target -> [(PackageName, Maybe Version)] -> ResultBody
+ Staversion.Internal.Result: Result :: PackageSource -> Query -> Maybe PackageSource -> Either ErrorMsg ResultBody -> Result
+ Staversion.Internal.Result: SimpleResultBody :: PackageName -> (Maybe Version) -> ResultBody
+ Staversion.Internal.Result: [resultBody] :: Result -> Either ErrorMsg ResultBody
+ Staversion.Internal.Result: [resultFor] :: Result -> Query
+ Staversion.Internal.Result: [resultIn] :: Result -> PackageSource
+ Staversion.Internal.Result: [resultReallyIn] :: Result -> Maybe PackageSource
+ Staversion.Internal.Result: data Result
+ Staversion.Internal.Result: data ResultBody
+ Staversion.Internal.Result: instance GHC.Classes.Eq Staversion.Internal.Result.Result
+ Staversion.Internal.Result: instance GHC.Classes.Eq Staversion.Internal.Result.ResultBody
+ Staversion.Internal.Result: instance GHC.Show.Show Staversion.Internal.Result.Result
+ Staversion.Internal.Result: instance GHC.Show.Show Staversion.Internal.Result.ResultBody
- Staversion.Internal.BuildPlan: packageVersion :: BuildPlan -> PackageName -> Maybe Version
+ Staversion.Internal.BuildPlan: packageVersion :: HasVersions t => t -> PackageName -> Maybe Version

Files

ChangeLog.md view
@@ -1,5 +1,11 @@ # Revision history for staversion +## 0.1.3.0  -- 2016-12-29++* Now staversion shows the exact resolver for a partial resolver (e.g. "lts-4" -> "lts-4.2")+* Now staversion reads .cabal files, and uses their `build-depends` fields as query.+* Fix minor error in ordering the result.+ ## 0.1.2.0  -- 2016-11-10  * New option `--hackage`, which searches hackage for the latest version number.
README.md view
@@ -2,8 +2,14 @@  ![travis status](https://api.travis-ci.org/debug-ito/staversion.png) -staversion is a command-line tool to look for version numbers of Haskell packages. It mainly focuses on package versions in stackage, i.e. it answers to questions like "What version is the package X in stackage lts-Y.ZZ?" It aims to make it easier to write `build-depends` section in YOUR_PACKAGE.cabal.+staversion is a command-line tool to look for version numbers of Haskell packages. +See `--help` message for detailed usage.++## Package version in Stackage++staversion mainly focuses on package versions in stackage, i.e. it answers to questions like "What version is the package X in stackage lts-Y.ZZ?" It aims to make it easier to write `build-depends` section in YOUR_PACKAGE.cabal.+     $ staversion --resolver lts-4.2 conduit     ------ lts-4.2     conduit ==1.2.6.1@@ -26,6 +32,8 @@  staversion first reads build plan YAML files that are stored locally in your computer, then it tries to fetch them over network. +## Package version in Hackage+ You can also look up the latest version numbers hosted on hackage.      $ staversion --hackage conduit base@@ -33,12 +41,35 @@     conduit ==1.2.8,     base ==4.9.0.0 -See `--help` message for detail.+## Package version for build-depends +You can also specify .cabal files in the query. In that case, staversion reads `build-depends` fields in all sections of those .cabal files, and shows versions of the dependency packages. +    $ staversion --hackage staversion.cabal +    ------ latest in hackage+    -- staversion.cabal - library+    base ==4.9.0.0,+    unordered-containers ==0.2.7.2,+    aeson ==1.0.2.1,+    text ==1.2.2.1,+    bytestring ==0.10.8.1,+    yaml ==0.8.21.1,+    filepath ==1.4.1.1,+    directory ==1.3.0.0,+    optparse-applicative ==0.13.0.0,+    containers ==0.5.9.1,+    http-client ==0.5.5,+    http-client-tls ==0.3.3,+    http-types ==0.9.1,+    transformers ==0.5.2.0,+    transformers-compat ==0.5.1.4,+    megaparsec ==5.1.2+    +    (snip)++ ## TODO -- Read `build-depends` sections .cabal files for package name queries. - Show version number ranges supported by the given resolvers. - Cache build plans in some local storage (SQLite?) 
src/Staversion/Internal/BuildPlan.hs view
@@ -4,18 +4,20 @@ -- Maintainer: Toshio Ito <debug.ito@gmail.com> -- -- __This is an internal module. End-users should not use it.__-{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE GeneralizedNewtypeDeriving, TupleSections #-} module Staversion.Internal.BuildPlan        ( -- * Entry APIs+         HasVersions(..),          BuildPlan,-         packageVersion,+         buildPlanSource,          BuildPlanManager,          newBuildPlanManager,          loadBuildPlan,          -- * Low-level APIs-         loadBuildPlanYAML,+         BuildPlanMap,+         loadBuildPlanMapYAML,          -- * For tests-         _setDisambiguator+         _setLTSDisambiguator        ) where  import Control.Applicative (empty, (<$>), (<*>))@@ -33,6 +35,7 @@ import Data.Text (Text, unpack) import Data.Traversable (Traversable(traverse)) import Data.Version (Version)+import Data.Word (Word) import qualified Data.Yaml as Yaml import System.FilePath ((</>), (<.>)) import qualified System.IO.Error as IOE@@ -54,18 +57,18 @@   ( Disambiguator,     fetchDisambiguator,     parseResolverString,-    formatResolverString,-    PartialResolver(..), ExactResolver,+    formatExactResolverString,+    PartialResolver(..), ExactResolver(..),     fetchBuildPlanYAML   ) import Staversion.Internal.BuildPlan.Version (unVersionJSON)  -- | A data structure that keeps a map between package names and their -- versions.-newtype BuildPlan = BuildPlan (HM.HashMap PackageName Version) deriving (Monoid)+newtype BuildPlanMap = BuildPlanMap (HM.HashMap PackageName Version) deriving (Monoid) -instance FromJSON BuildPlan where-  parseJSON (Object object) = (\p1 p2 -> BuildPlan $ p1 <> p2) <$> core_packages <*> other_packages where+instance FromJSON BuildPlanMap where+  parseJSON (Object object) = (\p1 p2 -> BuildPlanMap $ p1 <> p2) <$> core_packages <*> other_packages where     core_packages = parseSysInfo =<< (object .: "system-info")     parseSysInfo (Object o) = parseCorePackages =<< (o .: "core-packages")     parseSysInfo _ = empty@@ -79,6 +82,22 @@     parsePackageObject _ = empty   parseJSON _ = empty +-- | A 'BuildPlanMap' associated with its 'PackageSource'.+data BuildPlan = BuildPlan { buildPlanMap :: BuildPlanMap,+                             buildPlanSource :: PackageSource+                           }++-- | Types that have mapping between 'PackageName' and 'Version'.+class HasVersions t where+  packageVersion :: t -> PackageName -> Maybe Version++instance HasVersions BuildPlanMap where+  packageVersion (BuildPlanMap bp_map) name = HM.lookup name bp_map++instance HasVersions BuildPlan where+  packageVersion bp = packageVersion (buildPlanMap bp)++ -- | Stateful manager for 'BuildPlan's. data BuildPlanManager =   BuildPlanManager { manBuildPlanDir :: FilePath,@@ -129,18 +148,22 @@   handler e = return $ Left (context ++ ": " ++ show e)  loadBuildPlan :: BuildPlanManager-              -> [PackageName] -- ^ package names whose versions the user is interested in.-              -> PackageSource -> IO (Either ErrorMsg BuildPlan)+              -> [PackageName]+              -- ^ package names whose versions the user is interested in.+              -> PackageSource+              -> IO (Either ErrorMsg BuildPlan)+              -- ^ the second result is the real (disambiguated) PackageSource. loadBuildPlan man _ (SourceStackage resolver) = runExceptT impl where   impl = loadBuildPlan_stackageLocalFile man resolver `loggedElse'` do     e_resolver <- tryDisambiguate man =<< getPresolver-    loadBuildPlan_stackageLocalFile man (formatResolverString $ PartialExact e_resolver) `loggedElse'` loadBuildPlan_stackageNetwork man e_resolver+    loadBuildPlan_stackageLocalFile man (formatExactResolverString e_resolver) `loggedElse'` loadBuildPlan_stackageNetwork man e_resolver   getPresolver = maybeToLoadM ("Invalid resolver format for stackage.org: " ++ resolver) $ parseResolverString resolver   loggedElse' = loggedElse $ manLogger man loadBuildPlan man names SourceHackage = runExceptT impl where   impl = do     http_man <- httpManagerM man-    (mconcat . zipWith registeredVersionToBuildPlan names) <$> mapM (doFetch http_man) names+    build_plan_map <- (mconcat . zipWith registeredVersionToBuildPlanMap names) <$> mapM (doFetch http_man) names+    return $ BuildPlan { buildPlanMap = build_plan_map, buildPlanSource = SourceHackage }   logDebug' msg = liftIO $ logDebug (manLogger man) msg   doFetch http_man name = do     logDebug' ("Ask hackage for the latest version of " ++ unpack name)@@ -151,7 +174,9 @@   yaml_file = manBuildPlanDir man </> resolver <.> "yaml"   doLoad = do     logDebug (manLogger man) ("Read " ++ yaml_file ++ " for build plan.")-    loadBuildPlanYAML yaml_file+    e_build_plan_map <- loadBuildPlanMapYAML yaml_file+    return $ makeBuildPlan <$> e_build_plan_map+  makeBuildPlan bp_map = BuildPlan { buildPlanMap = bp_map, buildPlanSource = SourceStackage resolver }   handleIOError :: IOException -> Maybe ErrorMsg   handleIOError e | IOE.isDoesNotExistError e = Just $ makeErrorMsg e (yaml_file ++ " not found.")                   | IOE.isPermissionError e = Just $ makeErrorMsg e ("you cannot open " ++ yaml_file ++ ".")@@ -182,24 +207,33 @@   http_man <- httpManagerM man   liftIO $ logDebug (manLogger man) ("Fetch build plan from network: resolver = " ++ show e_resolver)   yaml_data <- httpExceptionToLoadM ("Downloading build plan failed: " ++ show e_resolver) $ liftIO $ fetchBuildPlanYAML http_man e_resolver-  ExceptT $ return $ parseBuildPlanYAML $ BSL.toStrict yaml_data+  makeBuildPlan <$> (ExceptT $ return $ parseBuildPlanMapYAML $ BSL.toStrict yaml_data)+  where+    makeBuildPlan bp_map = BuildPlan { buildPlanMap = bp_map,+                                       buildPlanSource = SourceStackage $ formatExactResolverString e_resolver+                                     } -parseBuildPlanYAML :: BS.ByteString -> Either ErrorMsg BuildPlan-parseBuildPlanYAML = either (Left . toErrorMsg) Right  . Yaml.decodeEither' where-  toErrorMsg parse_exception = "Error while parsing BuildPlan YAML: " ++ show parse_exception+parseBuildPlanMapYAML :: BS.ByteString -> Either ErrorMsg BuildPlanMap+parseBuildPlanMapYAML = either (Left . toErrorMsg) Right  . Yaml.decodeEither' where+  toErrorMsg parse_exception = "Error while parsing BuildPlanMap YAML: " ++ show parse_exception --- | Load a 'BuildPlan' from a file.-loadBuildPlanYAML :: FilePath -> IO (Either ErrorMsg BuildPlan)-loadBuildPlanYAML yaml_file = parseBuildPlanYAML <$> BS.readFile yaml_file where -- TODO: make it memory-efficient!+-- | Load a 'BuildPlanMap' from a file.+loadBuildPlanMapYAML :: FilePath -> IO (Either ErrorMsg BuildPlanMap)+loadBuildPlanMapYAML yaml_file = parseBuildPlanMapYAML <$> BS.readFile yaml_file where -- TODO: make it memory-efficient! -packageVersion :: BuildPlan -> PackageName -> Maybe Version-packageVersion (BuildPlan bp_map) name = HM.lookup name bp_map+registeredVersionToBuildPlanMap :: PackageName -> RegisteredVersions -> BuildPlanMap+registeredVersionToBuildPlanMap name rvers = BuildPlanMap $ HM.fromList $ pairs where+  pairs = case latestVersion rvers of+    Nothing -> []+    Just v -> [(name, v)]  _setDisambiguator :: BuildPlanManager -> Maybe Disambiguator -> IO () _setDisambiguator bp_man = writeIORef (manDisambiguator bp_man) -registeredVersionToBuildPlan :: PackageName -> RegisteredVersions -> BuildPlan-registeredVersionToBuildPlan name rvers = BuildPlan $ HM.fromList $ pairs where-  pairs = case latestVersion rvers of-    Nothing -> []-    Just v -> [(name, v)]+_setLTSDisambiguator :: BuildPlanManager+                     -> Word -- ^ disambiguated LTS major version+                     -> Word -- ^ disambiguated LTS minor version+                     -> IO ()+_setLTSDisambiguator bp_man lts_major lts_minor = _setDisambiguator bp_man $ Just disam where+  disam PartialLTSLatest = Just $ ExactLTS lts_major lts_minor+  disam _ = Nothing
src/Staversion/Internal/BuildPlan/Stackage.hs view
@@ -13,6 +13,7 @@          PartialResolver(..),          parseResolverString,          formatResolverString,+         formatExactResolverString,          Disambiguator,          fetchDisambiguator,          fetchBuildPlanYAML,@@ -84,6 +85,9 @@   PartialLTSLatest -> "lts"   PartialLTSMajor major -> "lts-" ++ show major   PartialNightlyLatest -> "nightly"++formatExactResolverString :: ExactResolver -> Resolver+formatExactResolverString er = formatResolverString $ PartialExact er  type Disambiguator = PartialResolver -> Maybe ExactResolver 
+ src/Staversion/Internal/Cabal.hs view
@@ -0,0 +1,137 @@+-- |+-- Module: Staversion.Internal.Cabal+-- Description: functions dealing with .cabal files.+-- Maintainer: Toshio Ito <debug.ito@gmail.com>+--+-- __This is an internal module. End-users should not use it.__+module Staversion.Internal.Cabal+       ( loadCabalFile,+         Target(..),+         BuildDepends(..)+       ) where++import Control.Applicative ((<*), (*>), (<|>), (<*>), many, some)+import Control.Monad (void, mzero, forM)+import Data.Bifunctor (first)+import Data.Char (isAlpha, isDigit, toLower, isSpace)+import Data.List (intercalate, nub)+import Data.Monoid (mconcat)+import Data.Text (pack, Text)+import qualified Data.Text as T+import qualified Data.Text.IO as TIO+import qualified Text.Megaparsec as P+import qualified Text.Megaparsec.Text as P++import Staversion.Internal.Query+  ( PackageName, ErrorMsg+  )++-- | Build target type.+data Target = TargetLibrary -- ^ the @library@ target.+            | TargetExecutable Text -- ^ the @executable NAME@ target.+            | TargetTestSuite Text -- ^ the @test-suite NAME@ target.+            | TargetBenchmark Text -- ^ the @benchmark NAME@ target.+            deriving (Show,Eq,Ord)++-- | A block of @build-depends:@.+data BuildDepends =+  BuildDepends { depsTarget :: Target,+                 depsPackages :: [PackageName]+               } deriving (Show,Eq,Ord)++loadCabalFile :: FilePath -> IO (Either ErrorMsg [BuildDepends])+loadCabalFile cabal_filepath = first show <$> P.runParser (cabalParser <* P.eof) cabal_filepath <$> TIO.readFile cabal_filepath++isLineSpace :: Char -> Bool+isLineSpace ' ' = True+isLineSpace '\t' = True+isLineSpace _ = False++indent :: P.Parser Int+indent = length <$> (many $ P.satisfy isLineSpace)++finishLine :: P.Parser ()+finishLine = P.eof <|> void P.eol++emptyLine :: P.Parser ()+emptyLine = indent *> (P.try finishLine <|> comment_line) where+  comment_line = P.string "--" *> P.manyTill P.anyChar finishLine *> pure ()++blockHeadLine :: P.Parser Target+blockHeadLine = target <* trail <* finishLine where+  trail = indent+  target = target_lib <|> target_exe <|> target_test <|> target_bench+  target_lib = P.try (P.string' "library") *> pure TargetLibrary+  target_exe = TargetExecutable <$> targetNamed "executable"+  target_test = TargetTestSuite <$> targetNamed "test-suite"+  target_bench = TargetBenchmark <$> targetNamed "benchmark"+  targetNamed :: String -> P.Parser Text+  targetNamed target_type = P.try (P.string' target_type)+                            *> (some $ P.satisfy isLineSpace)+                            *> (fmap pack $ some $ P.satisfy (not . isSpace))++fieldStart :: Maybe String -- ^ expected field name. If Nothing, it just don't care.+           -> P.Parser (String, Int) -- ^ (lower-case field name, indent level)+fieldStart mexp_name = do+  level <- indent+  name <- nameParser <* indent <* P.char ':'+  return (map toLower name, level)+  where+    nameParser = case mexp_name of+      Nothing -> some $ P.satisfy $ \c -> not (isLineSpace c || c == ':')+      Just exp_name -> P.string' exp_name++fieldBlock :: P.Parser (String, Text) -- ^ (lower-case field name, block content)+fieldBlock = impl where+  impl = do+    _ <- many $ P.try conditionalLine+    (field_name, level) <- P.try $ fieldStart Nothing+    field_trail <- P.manyTill P.anyChar finishLine+    rest <- remainingLines level+    let text_block = T.intercalate "\n" $ map pack (field_trail : rest)+    return (field_name, text_block)+  remainingLines field_indent_level = reverse <$> go [] where+    go cur_lines = (P.eof *> pure cur_lines) <|> foundSomething cur_lines+    foundSomething cur_lines = do+      void $ many $ P.try emptyLine+      this_level <- P.lookAhead indent+      if this_level <= field_indent_level+        then pure cur_lines+        else do+        _ <- indent+        this_line <- P.manyTill P.anyChar finishLine+        go (this_line : cur_lines)++buildDependsLine :: P.Parser [PackageName]+buildDependsLine = P.space *> (pname `P.endBy` ignored) where+  pname = pack <$> (some $ P.satisfy allowedChar)+  allowedChar '-' = True+  allowedChar '_' = True+  allowedChar c = isAlpha c || isDigit c+  ignored = P.manyTill P.anyChar finishItem *> P.space+  finishItem = P.eof <|> (void $ P.char ',')++conditionalLine :: P.Parser ()+conditionalLine = void $ indent *> (term "if" <|> term "else") *> P.manyTill P.anyChar finishLine where+  term :: String -> P.Parser ()+  term t = P.try (P.string' t *> P.lookAhead P.space)++targetBlock :: P.Parser BuildDepends+targetBlock = do+  target <- P.try blockHeadLine+  _ <- many $ P.try emptyLine+  fields <- some fieldBlock+  let build_deps_blocks = map snd $ filter (("build-depends" ==) . fst) $ fields+  packages <- fmap (nub . concat) $ forM build_deps_blocks $ \block -> do+    either (fail . show) return $ P.runParser (buildDependsLine <* P.space <* P.eof) "build-depends" block+  return $ BuildDepends { depsTarget = target,+                          depsPackages = packages+                        }++cabalParser :: P.Parser [BuildDepends]+cabalParser = reverse <$> go [] where+  go cur_deps = targetBlockParsed cur_deps <|> (P.eof *> pure cur_deps) <|> ignoreLine cur_deps+  targetBlockParsed cur_deps = do+    new_dep <- targetBlock+    go (new_dep : cur_deps)+  ignoreLine cur_deps = P.manyTill P.anyChar finishLine *> go cur_deps
src/Staversion/Internal/Command.hs view
@@ -25,6 +25,7 @@   ( Resolver,     PackageName,     Query(..),+    parseQuery,     PackageSource(..)   ) @@ -81,11 +82,15 @@                         Opt.short 'H',                         Opt.help "Search hackage.org for the latest version."                       ]-  queries = some $ QueryName <$> package-  package = fmap pack $ Opt.strArgument-            $ mconcat [ Opt.help "Name of package whose version you want to check.",-                        Opt.metavar "PACKAGE_NAME"-                      ]+  queries = some $ parseQuery <$> (query_package <|> query_cabal)+  query_package = Opt.strArgument+                  $ mconcat [ Opt.help "Name of package whose version you want to check.",+                              Opt.metavar "PACKAGE_NAME"+                            ]+  query_cabal = Opt.strArgument+                $ mconcat [ Opt.help "(EXPERIMENTAL) .cabal file name. It checks versions of packages in build-deps lists.",+                            Opt.metavar "CABAL_FILEPATH"+                          ]   network = not <$> no_network   no_network = Opt.switch $ mconcat [ Opt.long "no-network",                                       Opt.help "Forbid network access."
src/Staversion/Internal/Exec.hs view
@@ -6,7 +6,9 @@ -- __This is an internal module. End-users should not use it.__ module Staversion.Internal.Exec        ( main,-         processCommand+         processCommand,+         -- * Only for testing+         _processCommandWithCustomBuildPlanManager        ) where  import Control.Applicative ((<$>))@@ -16,48 +18,84 @@ import qualified Data.Text.Lazy.IO as TLIO  import Staversion.Internal.BuildPlan-  ( BuildPlan, packageVersion, newBuildPlanManager, loadBuildPlan+  ( BuildPlan, packageVersion, buildPlanSource,+    newBuildPlanManager, loadBuildPlan,+    BuildPlanManager   ) import Staversion.Internal.Command   ( parseCommandArgs,     Command(..)   ) import Staversion.Internal.Format (formatResultsCabal)-import Staversion.Internal.Log (logDebug, logError)+import Staversion.Internal.Log (logDebug, logError, Logger) import Staversion.Internal.Query-  ( Query(..), Result(..), PackageSource(..), PackageName,-    resultVersionsFromList, ResultVersions,-    ErrorMsg+  ( Query(..), PackageSource(..), PackageName, ErrorMsg   )+import Staversion.Internal.Result (Result(..), ResultBody(..))+import Staversion.Internal.Cabal (BuildDepends(..), loadCabalFile)  main :: IO () main = do   comm <- parseCommandArgs   (TLIO.putStr . formatResultsCabal) =<< (processCommand comm) +data ResolvedQuery = RQueryOne Query PackageName+                   | RQueryCabal Query FilePath BuildDepends+                   deriving (Show, Eq, Ord)+ processCommand :: Command -> IO [Result]-processCommand comm = impl where+processCommand = _processCommandWithCustomBuildPlanManager return++_processCommandWithCustomBuildPlanManager :: (BuildPlanManager -> IO BuildPlanManager) -> Command -> IO [Result]+_processCommandWithCustomBuildPlanManager customBPM comm = impl where   impl = do-    bp_man <- newBuildPlanManager (commBuildPlanDir comm) (commLogger comm) (commAllowNetwork comm)-    fmap concat $ mapM (processQueriesIn bp_man) $ commSources comm+    bp_man <- customBPM =<< newBuildPlanManager (commBuildPlanDir comm) (commLogger comm) (commAllowNetwork comm)+    rqueries <- resolveQueries' logger $ commQueries comm+    fmap concat $ mapM (processQueriesIn bp_man rqueries) $ commSources comm   logger = commLogger comm-  processQueriesIn bp_man source = do-    queried_names <- fmap (nub . concat) $ mapM getQueriedPackageNames $ commQueries comm+  processQueriesIn bp_man rqueries source = do+    let queried_names = nub $ concat $ map getQueriedPackageNames $ rqueries     logDebug logger ("Retrieve package source " ++ show source)     e_build_plan <- loadBuildPlan bp_man queried_names source     logBuildPlanResult e_build_plan-    return $ map (makeResult source e_build_plan) $ commQueries comm-  makeResult source e_build_plan query = case e_build_plan of-    Left error_msg -> Result { resultIn = source, resultFor = query, resultVersions = Left error_msg }-    Right build_plan -> Result { resultIn = source, resultFor = query,-                                 resultVersions = Right $ searchVersions build_plan query+    return $ map (makeResult source e_build_plan) $ rqueries+  makeResult source e_build_plan rquery = case e_build_plan of+    Left error_msg -> Result { resultIn = source, resultReallyIn = Nothing,+                               resultFor = originalQuery rquery, resultBody = Left error_msg+                             }+    Right build_plan -> Result { resultIn = source,+                                 resultReallyIn = if source == real_source then Nothing else Just real_source,+                                 resultFor = originalQuery rquery,+                                 resultBody = Right $ searchVersions build_plan rquery                                }+      where real_source = buildPlanSource build_plan   logBuildPlanResult (Right _) = logDebug logger ("Successfully retrieved build plan.")   logBuildPlanResult (Left error_msg) = logError logger ("Failed to load build plan: " ++ error_msg) -searchVersions :: BuildPlan -> Query -> ResultVersions-searchVersions build_plan (QueryName package_name) =-  resultVersionsFromList [(package_name, packageVersion build_plan package_name)]+resolveQueries' :: Logger -> [Query] -> IO [ResolvedQuery]+resolveQueries' logger = fmap concat . mapM resolveQ where+  resolveQ query = reportAndFilterError =<< resolveQuery logger query+  reportAndFilterError (Left err) = logError logger err >> return []+  reportAndFilterError (Right ret) = return ret -getQueriedPackageNames :: Query -> IO [PackageName]-getQueriedPackageNames (QueryName n) = return [n]+resolveQuery :: Logger -> Query -> IO (Either ErrorMsg [ResolvedQuery])+resolveQuery _ q@(QueryName name) = return $ Right $ [RQueryOne q name]+resolveQuery logger q@(QueryCabalFile file) = do+  logDebug logger ("Load " ++ file ++ " for build-depends fields.")+  (fmap . fmap) processBuildDependsList $ loadCabalFile file+  where+    processBuildDependsList = map (RQueryCabal q file) . filter ((0 <) . length . depsPackages)++originalQuery :: ResolvedQuery -> Query+originalQuery (RQueryOne q _) = q+originalQuery (RQueryCabal q _ _) = q++searchVersions :: BuildPlan -> ResolvedQuery -> ResultBody+searchVersions build_plan (RQueryOne _ package_name) = SimpleResultBody package_name $ packageVersion build_plan package_name+searchVersions build_plan (RQueryCabal _ cabal_file build_deps) = CabalResultBody cabal_file target ret_list where+  target = depsTarget build_deps+  ret_list = map (\pname -> (pname, packageVersion build_plan pname)) $ depsPackages build_deps++getQueriedPackageNames :: ResolvedQuery -> [PackageName]+getQueriedPackageNames (RQueryOne _ n) = [n]+getQueriedPackageNames (RQueryCabal _ _ bd) = depsPackages bd
src/Staversion/Internal/Format.hs view
@@ -13,18 +13,19 @@ import Data.Monoid (mempty, mconcat, (<>)) import qualified Data.Text.Lazy as TL import Data.Text.Lazy.Builder (Builder, toLazyText, fromText, fromString)-import Data.Version (showVersion)+import Data.Version (showVersion, Version)  import Staversion.Internal.Query-  ( Result(..), Query(..),+  ( Query(..),     sourceDesc,-    ResultVersions,-    resultVersionsToList+    PackageName   )+import Staversion.Internal.Result (Result(..), ResultBody(..))+import Staversion.Internal.Cabal (Target(..))  -- | format 'Result's like it's in build-depends in .cabal files. formatResultsCabal :: [Result] -> TL.Text-formatResultsCabal = toLazyText . mconcat . map formatGroupedResultsCabal . groupAllPreservingOrderBy ((==) `on` resultIn)+formatResultsCabal = toLazyText . mconcat . map formatResultBlock . makeSourceBlocks  groupAllPreservingOrderBy :: (a -> a -> Bool) -> [a] -> [[a]] groupAllPreservingOrderBy sameGroup = map snd  . foldr f [] where@@ -32,31 +33,63 @@     update heads [] = (item, [item]) : heads     update heads (cur@(cur_item, cur_list) : rest) =       if sameGroup item cur_item-      then heads ++ ( (cur_item, item : cur_list) : rest )+      then ((cur_item, item : cur_list) : heads) ++ rest        else update (heads ++ [cur]) rest -formatGroupedResultsCabal :: [Result] -> Builder-formatGroupedResultsCabal [] = mempty-formatGroupedResultsCabal results@(head_ret : _) = header <> (concatLines $ single_result_output =<< results) where-  header = "------ " <> (fromText $ sourceDesc $ resultIn head_ret) <> "\n"-  single_result_output ret = case resultVersions ret of-    Left _ -> [Left $ error_result ret]-    Right versions -> formatVersionsCabal (resultFor ret) versions-  error_result ret = case resultFor ret of-    QueryName query_name -> "-- " <> fromText query_name <> " ERROR"-  concatLines ebuilder_lines = (mconcat $ intersperse "\n" $ map (either id id) $ tailCommas ebuilder_lines) <> "\n\n"-  tailCommas = fst . foldr f ([], False) where-    -- flag: True if it has already encountered the last Right element in the list.-    f eb (ret, flag) = let (next_e, next_flag) = getNext ret flag eb-                       in (next_e:ret, next_flag)-    getNext [] flag e@(Left _) = (e, flag)-    getNext _ flag (Left b) = (Left (b <> ","), flag)-    getNext _ False e@(Right _) = (e, True)-    getNext _ True (Right b) = (Right (b <> ","), True)+-- | 'Left' lines and 'Right' lines are handled differently by+-- 'formatResultBlock'. It puts commas at the right places assuming+-- 'Left' lines are commented out.+type ResultLine = Either Builder Builder -formatVersionsCabal :: Query -> ResultVersions -> [Either Builder Builder]-formatVersionsCabal (QueryName _) rvers = map format $ resultVersionsToList rvers where-  format (name, mver) = case mver of-    Nothing -> Left $ "-- " <> fromText name <> " N/A"-    Just ver -> Right $ fromText name <> " ==" <> (fromString $ showVersion ver)+data ResultBlock = RBHead Builder [ResultBlock] -- ^ header and child blocks+                 | RBLines [ResultLine] -- ^ a block, which consists of some lines. +makeSourceBlocks :: [Result] -> [ResultBlock]+makeSourceBlocks = map sourceBlock . groupAllPreservingOrderBy ((==) `on` resultIn) where+  sourceBlock [] = RBLines []+  sourceBlock results@(head_ret : _) = RBHead header $ makeQueryBlocks results where+    header = "------ " <> (fromText $ sourceDesc $ resultIn head_ret) <> header_real_source+    header_real_source = maybe "" fromText $ resultReallyIn head_ret >>= \real_source -> do+      return (" (" <> sourceDesc real_source <> ")")++makeQueryBlocks :: [Result] -> [ResultBlock]+makeQueryBlocks = uncurry prependLines . foldr f ([], []) where+  prependLines blocks [] = blocks+  prependLines blocks rlines = (RBLines rlines) : blocks+  f ret (blocks, rlines) = case (resultFor ret, resultBody ret) of+    (_, Right (SimpleResultBody name mver)) -> (blocks, (versionLine name mver) : rlines)+    (_, Right (CabalResultBody file target pairs)) -> (cabalFileSuccessBlock file target pairs : prependLines blocks rlines, [])+    ((QueryName name), Left _) -> (blocks, (packageErrorLine name) : rlines)+    ((QueryCabalFile file), Left _) -> (cabalFileErrorBlock file : prependLines blocks rlines, [])++versionLine :: PackageName -> Maybe Version -> ResultLine+versionLine name Nothing = Left $ "-- " <> fromText name <> " N/A"+versionLine name (Just ver) = Right $ fromText name <> " ==" <> (fromString $ showVersion ver)++packageErrorLine :: PackageName -> ResultLine+packageErrorLine name = Left $ "-- " <> fromText name <> " ERROR"++cabalFileErrorBlock :: FilePath -> ResultBlock+cabalFileErrorBlock file = RBLines [Left line] where+  line = "-- " <> fromString file <> " ERROR"++cabalFileSuccessBlock :: FilePath -> Target -> [(PackageName, Maybe Version)] -> ResultBlock+cabalFileSuccessBlock file target pairs = RBHead header [RBLines $ map (uncurry versionLine) pairs] where+  header = "-- " <> fromString file <> " - " <> target_text+  target_text = case target of+    TargetLibrary -> "library"+    TargetExecutable n -> "executable " <> fromText n+    TargetTestSuite n -> "test-suite " <> fromText n+    TargetBenchmark n -> "benchmark " <> fromText n++formatResultBlock :: ResultBlock -> Builder+formatResultBlock (RBHead header blocks) = header <> "\n" <> mconcat (map formatResultBlock blocks)+formatResultBlock (RBLines rlines) = (mconcat $ map ((<> "\n") . either id id) $ tailCommas rlines) <> "\n" where+  tailCommas = fst . foldr f ([], False)+               -- flag: True if it has already encountered the last Right element in the list.+  f eb (ret, flag) = let (next_e, next_flag) = getNext ret flag eb+                     in (next_e:ret, next_flag)+  getNext [] flag e@(Left _) = (e, flag)+  getNext _ flag (Left b) = (Left (b <> ","), flag)+  getNext _ False e@(Right _) = (e, True)+  getNext _ True (Right b) = (Right (b <> ","), True)
src/Staversion/Internal/Query.hs view
@@ -8,18 +8,14 @@        ( PackageName,          Resolver,          PackageSource(..),+         sourceDesc,          Query(..),-         ErrorMsg,-         Result(..),-         ResultVersions,-         resultVersionsFromList,-         resultVersionsToList,-         sourceDesc+         parseQuery,+         ErrorMsg        ) where -import qualified Data.HashMap.Strict as HM+import Data.List (isSuffixOf) import Data.Text (Text, pack)-import Data.Version (Version)  type PackageName = Text @@ -33,28 +29,17 @@  -- | Query for package version(s). data Query = QueryName PackageName+           | QueryCabalFile FilePath            deriving (Show,Eq,Ord)  type ErrorMsg = String --- | Result for a query.-data Result = Result { resultIn :: PackageSource,-                       resultFor :: Query,-                       resultVersions :: Either ErrorMsg ResultVersions-                     } deriving (Show,Eq)----- | The obtained version map.-newtype ResultVersions = ResultVersions (HM.HashMap PackageName (Maybe Version))-                       deriving (Show,Eq)--resultVersionsFromList :: [(PackageName, Maybe Version)] -> ResultVersions-resultVersionsFromList = ResultVersions . HM.fromList--resultVersionsToList :: ResultVersions -> [(PackageName, Maybe Version)]-resultVersionsToList (ResultVersions m) = HM.toList m- -- | description of a 'PackageSource'. sourceDesc :: PackageSource -> Text sourceDesc (SourceStackage r) = pack r sourceDesc SourceHackage = "latest in hackage"++parseQuery :: String -> Query+parseQuery s = if ".cabal" `isSuffixOf` s+               then QueryCabalFile s+               else QueryName $ pack s
+ src/Staversion/Internal/Result.hs view
@@ -0,0 +1,28 @@+-- |+-- Module: Staversion.Internal.Result+-- Description: Result data type and its utilities+-- Maintainer: Toshio Ito <debug.ito@gmail.com>+--+-- __This is an internal module. End-users should not use it.__+module Staversion.Internal.Result+       ( Result(..),+         ResultBody(..)+       ) where++import Data.Version (Version)+import Staversion.Internal.Query+  ( Query, PackageSource, ErrorMsg, PackageName+  )+import Staversion.Internal.Cabal (Target)++-- | Result for a query.+data Result = Result { resultIn :: PackageSource,+                       resultFor :: Query,+                       resultReallyIn :: Maybe PackageSource,+                       -- ^ the true PackageSource resolved (or redirected) from 'resultIn', if any.+                       resultBody :: Either ErrorMsg ResultBody+                     } deriving (Show,Eq)++data ResultBody = SimpleResultBody PackageName (Maybe Version)+                | CabalResultBody FilePath Target [(PackageName, (Maybe Version))]+                deriving (Show,Eq)
staversion.cabal view
@@ -1,5 +1,5 @@ name:                   staversion-version:                0.1.2.0+version:                0.1.3.0 author:                 Toshio Ito <debug.ito@gmail.com> maintainer:             Toshio Ito <debug.ito@gmail.com> license:                BSD3@@ -16,7 +16,10 @@                         test/data/lts-4.2.yaml,                         test/data/lts-2.22_conpact.yaml,                         test/data/snapshots.json,-                        test/data/aeson_preferred.json+                        test/data/aeson_preferred.json,+                        test/data/doctest.cabal_test,+                        test/data/conduit.cabal_test,+                        test/data/foobar.cabal_test                          homepage:               https://github.com/debug-ito/staversion bug-reports:            https://github.com/debug-ito/staversion/issues@@ -26,12 +29,14 @@   hs-source-dirs:       src   ghc-options:          -Wall -fno-warn-unused-imports   default-extensions:   OverloadedStrings-  other-extensions:     CPP, DeriveDataTypeable, GeneralizedNewtypeDeriving+  other-extensions:     CPP, DeriveDataTypeable, TupleSections   exposed-modules:      Staversion.Internal.BuildPlan,                         Staversion.Internal.BuildPlan.Stackage,                         Staversion.Internal.BuildPlan.Hackage,                         Staversion.Internal.BuildPlan.Version,                         Staversion.Internal.Query,+                        Staversion.Internal.Result,+                        Staversion.Internal.Cabal,                         Staversion.Internal.Command,                         Staversion.Internal.Log,                         Staversion.Internal.Exec,@@ -45,14 +50,15 @@                         bytestring >=0.10.0 && <0.11,                         yaml >=0.8.3 && <0.9,                         filepath >=1.3.0 && <1.5,-                        directory >=1.2.0 && <1.3,+                        directory >=1.2.0 && <1.4,                         optparse-applicative >=0.11.0 && <0.14,                         containers >=0.5.5 && <0.6,                         http-client >=0.4.18 && <0.6,                         http-client-tls >=0.2.2 && <0.4,                         http-types >=0.8.6 && <0.10,                         transformers >=0.3.0 && <0.6,-                        transformers-compat >=0.4.0 && <0.6+                        transformers-compat >=0.4.0 && <0.6,+                        megaparsec >=4.2.0 && <5.2  executable staversion   default-language:     Haskell2010@@ -78,10 +84,11 @@                         Staversion.Internal.BuildPlan.VersionSpec,                         Staversion.Internal.ExecSpec,                         Staversion.Internal.FormatSpec,+                        Staversion.Internal.CabalSpec,                         Staversion.Internal.TestUtil   build-depends:        base, staversion, text, filepath, bytestring,                         hspec >=2.1.7 && <2.4,-                        QuickCheck+                        QuickCheck >=2.8.1 && <2.10  flag network-test   description: Enable network tests.
test/NetworkTest.hs view
@@ -1,7 +1,9 @@ module Main (main,spec) where  import Control.Applicative ((<$>))+import Control.Monad (forM_) import qualified Data.ByteString.Lazy as BSL+import Data.Word (Word) import Network.HTTP.Client (newManager, Manager) import Network.HTTP.Client.TLS (tlsManagerSettings) import Test.Hspec@@ -12,21 +14,23 @@ import Staversion.Internal.BuildPlan   ( newBuildPlanManager,     loadBuildPlan,-    packageVersion+    packageVersion,+    buildPlanSource   ) import Staversion.Internal.BuildPlan.Hackage (fetchPreferredVersions, latestVersion) import Staversion.Internal.BuildPlan.Stackage   ( fetchDisambiguator,     fetchBuildPlanYAML,-    PartialResolver(..), ExactResolver(..)+    PartialResolver(..), ExactResolver(..),+    parseResolverString   ) import Staversion.Internal.Command (Command(..)) import Staversion.Internal.Exec (processCommand) import Staversion.Internal.Log (defaultLogger, Logger(loggerThreshold)) import Staversion.Internal.Query- ( PackageSource(..), ErrorMsg, Query(..), Result(..),-   resultVersionsToList+ ( PackageSource(..), ErrorMsg, Query(..)  )+import Staversion.Internal.Result (Result(..), ResultBody(..))  main :: IO () main = hspec spec@@ -45,7 +49,10 @@       e_dis <- fetchDisambiguator man       case e_dis of        Left err -> expectationFailure ("should not be Left: " ++ err)-       Right dis -> dis (PartialLTSMajor 2) `shouldBe` Just (ExactLTS 2 22)+       Right dis -> forM_ [(0,7), (1,15), (2,22), (3,22), (4,2), (5,18), (6,24), (7,8)] $ \(major, minor_min) -> do+         case dis (PartialLTSMajor major) of+          Just eresolver -> (PartialExact eresolver) `shouldBeAboveLTSMinorResolver` (major,minor_min)+          Nothing -> expectationFailure "Unexpected disambiguation error."   describe "fetchBuildPlanYAML" $ do     it "fetches a LTS build plan" $ \man -> do       raw_yaml <- fetchBuildPlanYAML man (ExactLTS 2 22)@@ -66,19 +73,34 @@ isJustAnd :: Maybe a -> (a -> Bool) -> Bool isJustAnd m p = maybe False p m +shouldBeAboveLTSMinorResolver :: PartialResolver -> (Word,Word) -> IO ()+shouldBeAboveLTSMinorResolver (PartialExact (ExactLTS got_major got_minor)) (lts_major, lts_minor_min) = do+  got_major `shouldBe` lts_major+  got_minor `shouldSatisfy` (>= lts_minor_min)+shouldBeAboveLTSMinorResolver pr _ = expectationFailure ("Unexpected PartialResolver: " ++ show pr)++shouldBeAboveLTSMinor :: PackageSource -> (Word, Word) -> IO ()+shouldBeAboveLTSMinor (SourceStackage resolver) expected_lts =+  case parseResolverString resolver of+   Just presolver -> presolver `shouldBeAboveLTSMinorResolver` expected_lts+   ret_parse -> expectationFailure ("Unexpected parse result: " ++ show ret_parse)+shouldBeAboveLTSMinor source _ = expectationFailure ("Unexpected PackageSource: " ++ show source)+ spec_BuildPlan :: Spec spec_BuildPlan = describe "BuildPlan" $ do   describe "loadBuildPlan from Stackage" $ do     it "disambiguates LTS version and fetches a valid BuildPlan" $ do       bp_man <- newBuildPlanManager "." quietLogger True       bp <- expectRight "loadBuildPlan failed: " =<< loadBuildPlan bp_man [] (SourceStackage "lts-5")-      packageVersion bp "base" `shouldBe` Just (ver [4,8,2,0])-      packageVersion bp "bytestring" `shouldBe` Just (ver [0,10,6,0])-      packageVersion bp "conduit" `shouldBe` Just (ver [1,2,6,6])+      buildPlanSource bp `shouldBeAboveLTSMinor` (5,18)+      packageVersion bp "base" `shouldSatisfy` (`isJustAnd` (>= ver [4,8,2,0]))+      packageVersion bp "bytestring" `shouldSatisfy` (`isJustAnd` (>= ver [0,10,6,0]))+      packageVersion bp "conduit" `shouldSatisfy` (`isJustAnd` (>= ver [1,2,6,6]))   describe "loadBuildPlan from Hackage" $ do     it "fetches BuildPlan for queried packages" $ do       bp_man <- newBuildPlanManager "." quietLogger True       bp <- expectRight "loadBuildPlan failed: " =<< loadBuildPlan bp_man ["base", "lens", "transformers"] SourceHackage+      buildPlanSource bp `shouldBe` SourceHackage       packageVersion bp "base" `shouldSatisfy` (`isJustAnd` (>= ver [4,9,0,0]))       packageVersion bp "lens" `shouldSatisfy` (`isJustAnd` (>= ver [4,15,1]))       packageVersion bp "transformers" `shouldSatisfy` (`isJustAnd` (>= ver [0,5,2,0]))@@ -95,9 +117,28 @@  spec_Exec :: Spec spec_Exec = describe "Exec" $ describe "processCommand" $ do-  specify "search hackage" $ do+  it "should fill resultReallyIn field if necesssary" $ do     let comm = Command { commBuildPlanDir = ".",                          commLogger = quietLogger,+                         commSources = [SourceStackage "lts-3"],+                         commQueries = [QueryName "base"],+                         commAllowNetwork = True+                       }+    [ret] <- processCommand comm+    resultIn ret `shouldBe` SourceStackage "lts-3"+    resultFor ret `shouldBe` QueryName "base"+    case resultBody ret of+     Right (SimpleResultBody got_name (Just got_version)) -> do+       got_name `shouldBe` "base"+       got_version `shouldSatisfy` (>= ver [4,8,1,0])+     body -> expectationFailure ("Unexpected body: " ++ show body)+    case resultReallyIn ret of+     Just source -> source `shouldBeAboveLTSMinor` (3,22)+     ret_really_in -> expectationFailure ("Unexpected resultReallyIn: " ++ show ret_really_in)++  it "should search hackage" $ do+    let comm = Command { commBuildPlanDir = ".",+                         commLogger = quietLogger,                          commSources = [SourceHackage],                          commQueries = [QueryName "base"],                          commAllowNetwork = True@@ -105,8 +146,13 @@     [ret] <- processCommand comm     resultIn ret `shouldBe` SourceHackage     resultFor ret `shouldBe` QueryName "base"-    [(got_name, Just got_version)] <- resultVersionsToList <$> expectRight "processCommand error" (resultVersions ret)-    got_name `shouldBe` "base"-    got_version `shouldSatisfy` (>= ver [4,9,0,0]) +    resultReallyIn ret `shouldBe` Nothing+    case resultBody ret of+     Right (SimpleResultBody got_name (Just got_version)) -> do+       got_name `shouldBe` "base"+       got_version `shouldSatisfy` (>= ver [4,9,0,0])+     body -> expectationFailure ("Unexpected body: " ++ show body)+    +              
test/Staversion/Internal/BuildPlanSpec.hs view
@@ -2,23 +2,21 @@  import Data.Text (Text, pack) import Data.Version (Version(..))+import Data.Word (Word) import System.FilePath ((</>), (<.>)) import Test.Hspec  import Staversion.Internal.Log (defaultLogger, loggerThreshold) import Staversion.Internal.Query (PackageName, PackageSource(..)) import Staversion.Internal.BuildPlan-  ( BuildPlan, -    loadBuildPlanYAML, +  ( BuildPlanMap, +    loadBuildPlanMapYAML,      packageVersion,     BuildPlanManager,     newBuildPlanManager,-    _setDisambiguator,-    loadBuildPlan-  )-import Staversion.Internal.BuildPlan.Stackage-  ( Disambiguator,-    PartialResolver(..), ExactResolver(..)+    _setLTSDisambiguator,+    loadBuildPlan,+    buildPlanSource   )  main :: IO ()@@ -31,7 +29,7 @@  packageVersion_spec :: Spec packageVersion_spec = describe "packageVersion" $ do-  forBuildPlan "conpact_build_plan" $ \loader -> do+  forBuildPlanMap "conpact_build_plan" $ \loader -> do     specify "drawille -> 0.1.0.6" $ do       loadVersion "drawille" loader `shouldReturn` Just (Version [0,1,0,6] [])     specify "unknown -> Nothing" $ do@@ -41,27 +39,27 @@     specify "base -> 4.8.2.0" $ do       loadVersion "base" loader `shouldReturn` Just (Version [4,8,2,0] []) -  forBuildPlan "lts-4.2" $ \loader -> do+  forBuildPlanMap "lts-4.2" $ \loader -> do     specify "conduit -> 1.2.6.1" $ do       loadVersion "conduit" loader `shouldReturn` Just (Version [1,2,6,1] [])     specify "transformers -> 0.4.2.0" $ do       loadVersion "transformers" loader `shouldReturn` Just (Version [0,4,2,0] [])  -forBuildPlan :: String -> (IO BuildPlan -> Spec) -> Spec-forBuildPlan build_plan_base testWith = describe build_plan_base (testWith loader) where-  loader = either error return =<< loadBuildPlanYAML ("test" </> "data" </> build_plan_base <.> "yaml")+forBuildPlanMap :: String -> (IO BuildPlanMap -> Spec) -> Spec+forBuildPlanMap build_plan_base testWith = describe build_plan_base (testWith loader) where+  loader = either error return =<< loadBuildPlanMapYAML ("test" </> "data" </> build_plan_base <.> "yaml") -loadVersion :: PackageName -> IO BuildPlan -> IO (Maybe Version)+loadVersion :: PackageName -> IO BuildPlanMap -> IO (Maybe Version) loadVersion package_name loader = do   plan <- loader   return $ packageVersion plan package_name  -mockBuildPlanManager :: Disambiguator -> IO BuildPlanManager-mockBuildPlanManager disam = do+mockBuildPlanManager :: Word -> Word -> IO BuildPlanManager+mockBuildPlanManager lts_major lts_minor = do   bp_man <- newBuildPlanManager build_plan_dir logger False-  _setDisambiguator bp_man (Just disam)+  _setLTSDisambiguator bp_man lts_major lts_minor   return bp_man   where     build_plan_dir = "test" </> "data"@@ -70,8 +68,7 @@ loadBuildPlan_spec :: Spec loadBuildPlan_spec = describe "loadBuildPlan" $ do   it "reads local file after disambiguation" $ do-    let disam (PartialLTSLatest) = Just $ ExactLTS 4 2-        disam _ = Nothing-    bp_man <- mockBuildPlanManager disam+    bp_man <- mockBuildPlanManager 4 2     bp <- either (\e -> error ("Error: " ++ e)) return =<< loadBuildPlan bp_man [] (SourceStackage "lts")     packageVersion bp "base" `shouldBe` (Just $ Version [4,8,2,0] [])+    buildPlanSource bp `shouldBe` SourceStackage "lts-4.2"
+ test/Staversion/Internal/CabalSpec.hs view
@@ -0,0 +1,112 @@+module Staversion.Internal.CabalSpec (main,spec) where++import System.FilePath ((</>), (<.>))+import Test.Hspec++import Staversion.Internal.Cabal (loadCabalFile, BuildDepends(..), Target(..))++main :: IO ()+main = hspec spec++shouldBeParsedTo :: FilePath -> [BuildDepends] -> IO ()+shouldBeParsedTo cabal_file expectation = do+  got_deps <- either error return  =<< loadCabalFile ("test" </> "data" </> cabal_file)+  got_deps `shouldBe` expectation++spec :: Spec+spec = describe "loadCabalFile" $ do+  it "should load library, executable and test-suite targets" $ do+    "doctest.cabal_test" `shouldBeParsedTo`+      [ BuildDepends { depsTarget = TargetLibrary,+                       depsPackages = [ "base",+                                        "base-compat",+                                        "ghc",+                                        "syb",+                                        "deepseq",+                                        "directory",+                                        "filepath",+                                        "process",+                                        "ghc-paths",+                                        "transformers"+                                      ]+                     },+        BuildDepends { depsTarget = TargetExecutable "doctest",+                       depsPackages = [ "base",+                                        "doctest"+                                      ]+                     },+        BuildDepends { depsTarget = TargetTestSuite "spec",+                       depsPackages = [ "base",+                                        "ghc",+                                        "syb",+                                        "deepseq",+                                        "directory",+                                        "filepath",+                                        "process",+                                        "ghc-paths",+                                        "transformers",+                                        "base-compat",+                                        "HUnit",+                                        "hspec",+                                        "QuickCheck",+                                        "stringbuilder",+                                        "silently",+                                        "setenv",+                                        "with-location"+                                      ]+                     },+        BuildDepends { depsTarget = TargetTestSuite "doctests",+                       depsPackages = [ "base", "doctest"+                                      ]+                     }+      ]+  it "should merge conditional build-depends" $ do+    "conduit.cabal_test" `shouldBeParsedTo`+      [ BuildDepends { depsTarget = TargetLibrary,+                       depsPackages = [ "base",+                                        "resourcet",+                                        "exceptions",+                                        "lifted-base",+                                        "transformers-base",+                                        "transformers",+                                        "mtl",+                                        "mmorph",+                                        "monad-control",+                                        "void"+                                      ]+                     },+        BuildDepends { depsTarget = TargetTestSuite "test",+                       depsPackages = [ "conduit",+                                        "base",+                                        "hspec",+                                        "QuickCheck",+                                        "transformers",+                                        "mtl",+                                        "resourcet",+                                        "containers",+                                        "exceptions",+                                        "safe",+                                        "void"+                                      ]+                     },+        BuildDepends { depsTarget = TargetBenchmark "optimize-201408",+                       depsPackages = [ "base",+                                        "conduit",+                                        "vector",+                                        "deepseq",+                                        "containers",+                                        "transformers",+                                        "hspec",+                                        "mwc-random",+                                        "criterion",+                                        "kan-extensions"+                                      ]+                     },+        BuildDepends { depsTarget = TargetBenchmark "unfused",+                       depsPackages = [ "base",+                                        "conduit",+                                        "criterion",+                                        "transformers"+                                      ]+                     }+      ]
test/Staversion/Internal/ExecSpec.hs view
@@ -6,32 +6,43 @@ import System.FilePath ((</>)) import Test.Hspec +import Staversion.Internal.BuildPlan (_setLTSDisambiguator) import Staversion.Internal.Command (Command(..))-import Staversion.Internal.Exec (processCommand)+import Staversion.Internal.Exec+  ( processCommand,+    _processCommandWithCustomBuildPlanManager+  ) import Staversion.Internal.Query   ( PackageName,     Query(..),     PackageSource(..),-    Result(..),-    ResultVersions,-    resultVersionsFromList,     ErrorMsg   )+import Staversion.Internal.Result+  ( Result(..),+    ResultBody(..)+  ) import Staversion.Internal.Log (defaultLogger, _mockLogger, Logger(loggerThreshold), LogLevel(..))+import Staversion.Internal.Cabal (Target(..)) -import Staversion.Internal.TestUtil (ver, rvers)+import Staversion.Internal.TestUtil (ver, simpleResultBody, verPairs)  main :: IO () main = hspec spec  spec :: Spec-spec = describe "processCommand" $ do+spec = do+  spec_processCommand_basic+  spec_processCommand_disambiguates++spec_processCommand_basic :: Spec+spec_processCommand_basic = describe "processCommand" $ do   specify "QueryName, SourceStackage, hit" $ do     singleCase (SourceStackage "conpact_build_plan") (QueryName "drawille")-      (Right $ rvers [("drawille", Just $ ver [0,1,0,6])])+      (Right $ simpleResultBody "drawille" [0,1,0,6])   specify "QueryName, SourceStackage, miss" $ do     singleCase (SourceStackage "conpact_build_plan") (QueryName "unknown")-      (Right $ rvers [("unknown", Nothing)])+      (Right $ SimpleResultBody "unknown" Nothing)   specify "QueryName, SourceStackage, source not found" $ do     (logger, logs) <- _mockLogger     let src = SourceStackage "unknown"@@ -43,7 +54,7 @@     [got_ret] <- processCommand comm     resultIn got_ret `shouldBe` src     resultFor got_ret `shouldBe` query-    case resultVersions got_ret of+    case resultBody got_ret of       Right _ -> expectationFailure "it should fail"       Left _ -> return ()     got_logs <- readIORef logs@@ -58,32 +69,55 @@         qc = QueryName "conduit"         qa = QueryName "aeson"         comm = baseCommand { commSources = [src2, src7], commQueries = [qc, qa] }-        expected = [ Result { resultIn = src2, resultFor = qc,-                              resultVersions = Right $ rvers [("conduit", Just $ ver [1,2,5])]+        expected = [ Result { resultIn = src2, resultReallyIn = Nothing, resultFor = qc,+                              resultBody = Right $ simpleResultBody "conduit" [1,2,5]                             },-                     Result { resultIn = src2, resultFor = qa,-                              resultVersions = Right $ rvers [("aeson", Just $ ver [0,8,0,2])]+                     Result { resultIn = src2, resultReallyIn = Nothing, resultFor = qa,+                              resultBody = Right $ simpleResultBody "aeson" [0,8,0,2]                             },-                     Result { resultIn = src7, resultFor = qc,-                              resultVersions = Right $ rvers [("conduit", Just $ ver [1,2,7])]+                     Result { resultIn = src7, resultReallyIn = Nothing, resultFor = qc,+                              resultBody = Right $ simpleResultBody "conduit" [1,2,7]                             },-                     Result { resultIn = src7, resultFor = qa,-                              resultVersions = Right $ rvers [("aeson", Just $ ver [0,11,2,1])]+                     Result { resultIn = src7, resultReallyIn = Nothing, resultFor = qa,+                              resultBody = Right $ simpleResultBody "aeson" [0,11,2,1]                             }                    ]-    got <- processCommand comm-    got `shouldBe` expected+    processCommand comm `shouldReturn` expected+  specify "QueryCabalfile, SourceStackage" $ do+    let src = SourceStackage "lts-4.2"+        cabal_file = ("test" </> "data" </> "foobar.cabal_test")+        query = QueryCabalFile cabal_file+        comm = baseCommand { commSources = [src], commQueries = [query] }+        ret t vps = Result { resultIn = src, resultReallyIn = Nothing, resultFor = query,+                             resultBody = Right $ CabalResultBody cabal_file t vps+                           }+        expected = [ ret TargetLibrary $ verPairs [ ("base", [4,8,2,0]),+                                                    ("unordered-containers", [0,2,5,1])+                                                  ],+                     -- no result for "executable" section because it has no build-depends field.+                     ret (TargetTestSuite "spec") $ verPairs [ ("base", [4,8,2,0]),+                                                               ("staversion", []),+                                                               ("text", [1,2,2,0]),+                                                               ("filepath", [1,4,0,0]),+                                                               ("bytestring", [0,10,6,0])+                                                             ],+                     ret (TargetTestSuite "network-test") $ verPairs [ ("base", [4,8,2,0]),+                                                                       ("http-client", [0,4,26,2])+                                                                     ]+                   ]+    processCommand comm `shouldReturn` expected          -singleCase :: PackageSource -> Query -> Either ErrorMsg ResultVersions -> IO ()+singleCase :: PackageSource -> Query -> Either ErrorMsg ResultBody -> IO () singleCase src query exp_ret_vers = singleCase' src query (`shouldBe` exp_ret_vers) -singleCase' :: PackageSource -> Query -> (Either ErrorMsg ResultVersions -> IO a) -> IO a+singleCase' :: PackageSource -> Query -> (Either ErrorMsg ResultBody -> IO a) -> IO a singleCase' src query checker = do   [got_ret] <- processCommand comm   resultIn got_ret `shouldBe` src+  resultReallyIn got_ret `shouldBe` Nothing   resultFor got_ret `shouldBe` query-  checker $ resultVersions got_ret+  checker $ resultBody got_ret   where     comm =  baseCommand { commSources = [src],                           commQueries = [query]@@ -96,3 +130,18 @@                         commQueries = [],                         commAllowNetwork = False                       }++spec_processCommand_disambiguates :: Spec+spec_processCommand_disambiguates = describe "processCommand" $ do+  it "disambiguates a partial resolver and sets resultReallyIn field" $ do+    let comm = baseCommand { commSources = [SourceStackage "lts"],+                             commQueries = [QueryName "conduit"]+                           }+        withMockDisam bp_man = do+          _setLTSDisambiguator bp_man 4 2+          return bp_man+    [got_ret] <- _processCommandWithCustomBuildPlanManager withMockDisam comm+    resultIn got_ret `shouldBe` SourceStackage "lts"+    resultReallyIn got_ret `shouldBe` (Just $ SourceStackage "lts-4.2")+    resultFor got_ret `shouldBe` QueryName "conduit"+    resultBody got_ret `shouldBe` Right (simpleResultBody "conduit" [1,2,6,1])
test/Staversion/Internal/FormatSpec.hs view
@@ -5,11 +5,13 @@  import Staversion.Internal.Format (formatResultsCabal) import Staversion.Internal.Query-  ( Result(..), PackageSource(..), Query(..),+  ( PackageSource(..), Query(..),     Resolver, PackageName   )+import Staversion.Internal.Result (Result(..), ResultBody(..))+import Staversion.Internal.Cabal (Target(..)) -import Staversion.Internal.TestUtil (ver, rvers)+import Staversion.Internal.TestUtil (ver, simpleResultBody, verPairs)          @@ -21,9 +23,9 @@   it "should return empty text for empty list" $ do     formatResultsCabal [] `shouldBe` ""   it "should format a Result in a Cabal way" $ do-    let input = [ Result { resultIn = SourceStackage "lts-6.10",+    let input = [ Result { resultIn = SourceStackage "lts-6.10", resultReallyIn = Nothing,                            resultFor = QueryName "hoge",-                           resultVersions = Right $ rvers [("hoge", Just $ ver [3,4,5])]+                           resultBody = Right $ simpleResultBody "hoge" [3,4,5]                          }                 ]         expected = ( "------ lts-6.10\n"@@ -56,6 +58,30 @@                      <> "\n"                    )     formatResultsCabal input `shouldBe` expected+  it "should group Results by PackageSource with preserved order (2)" $ do+    let input = [ simpleResult "lts-5.0" "pack-A" [1,0],+                  simpleResult "lts-5.0" "pack-A" [2,0],+                  simpleResult "lts-6.6" "pack-A" [3,0],+                  simpleResult "lts-6.6" "pack-A" [4,0],+                  simpleResult "lts-8.0" "pack-A" [5,0],+                  simpleResult "lts-6.6" "pack-A" [6,0],+                  simpleResult "lts-6.6" "pack-A" [7,0]+                ]+        expected = ( "------ lts-5.0\n"+                     <> "pack-A ==1.0,\n"+                     <> "pack-A ==2.0\n"+                     <> "\n"+                     <> "------ lts-6.6\n"+                     <> "pack-A ==3.0,\n"+                     <> "pack-A ==4.0,\n"+                     <> "pack-A ==6.0,\n"+                     <> "pack-A ==7.0\n"+                     <> "\n"+                     <> "------ lts-8.0\n"+                     <> "pack-A ==5.0\n"+                     <> "\n"+                   )+    formatResultsCabal input `shouldBe` expected   it "should not put comma at the last non-N/A entry even if it is followed by N/A entries"  $ do     let input = [ simpleResult "s" "hoge" [1,0,0],                   simpleResult "s" "not-found-1" [],@@ -72,13 +98,129 @@                      <> "\n"                    )     formatResultsCabal input `shouldBe` expected+  it "should output resultReallyIn field" $ do+    let input = [ Result { resultIn = SourceStackage "lts",+                           resultReallyIn = Just $ SourceStackage "lts-7.4",+                           resultFor = QueryName "foobar",+                           resultBody = Right $ simpleResultBody "foobar" [3,4,5]+                         } ]+        expected = ( "------ lts (lts-7.4)\n"+                     <> "foobar ==3.4.5\n"+                     <> "\n"+                   )+    formatResultsCabal input `shouldBe` expected+  it "should show ERROR if resultBody is Left, resultFor is QueryName" $ do+    let input = [ Result { resultIn = SourceStackage "lts-4.2", resultReallyIn = Nothing,+                           resultFor = QueryName "hogehoge",+                           resultBody = Left "some error"+                         } ]+        expected = ( "------ lts-4.2\n"+                     <> "-- hogehoge ERROR\n"+                     <> "\n"+                   )+    formatResultsCabal input `shouldBe` expected+  it "should show ERROR if resultBody is Left, resultFor is QueryCabalFile" $ do+    let input = [ Result { resultIn = SourceStackage "lts-5.3", resultReallyIn = Nothing,+                           resultFor = QueryCabalFile "foobar.cabal",+                           resultBody = Left "some error"+                         },+                  Result { resultIn = SourceStackage "lts-5.3", resultReallyIn = Nothing,+                           resultFor = QueryName "hoge",+                           resultBody = Right $ simpleResultBody "hoge" [5,5]+                         }+                ]+        expected = ( "------ lts-5.3\n"+                     <> "-- foobar.cabal ERROR\n"+                     <> "\n"+                     <> "hoge ==5.5\n"+                     <> "\n"+                   )+    formatResultsCabal input `shouldBe` expected+  it "should show build-depends blocks for CabalResultBody" $ do+    let mkRet = cabalResult "lts-7.0" "hoge.cabal"+        input = [ mkRet TargetLibrary [ ("base", [4,6,0,0]),+                                        ("foobar", [5,7])+                                      ],+                  mkRet (TargetExecutable "hoge-exe") [ ("bytestring", [1,9]) ],+                  mkRet (TargetTestSuite "hoge-test") [ ("hspec", [10,8,9]),+                                                        ("QuickCheck", [5,4,2]),+                                                        ("unknown", [])+                                                      ],+                  mkRet (TargetBenchmark "hoge-bench") [ ("base", [4,8,0,4]),+                                                         ("quux", []),+                                                         ("parsec", [3,0,2])+                                                       ]+                ]+        expected = ( "------ lts-7.0\n"+                     <> "-- hoge.cabal - library\n"+                     <> "base ==4.6.0.0,\n"+                     <> "foobar ==5.7\n"+                     <> "\n"+                     <> "-- hoge.cabal - executable hoge-exe\n"+                     <> "bytestring ==1.9\n"+                     <> "\n"+                     <> "-- hoge.cabal - test-suite hoge-test\n"+                     <> "hspec ==10.8.9,\n"+                     <> "QuickCheck ==5.4.2\n"+                     <> "-- unknown N/A\n"+                     <> "\n"+                     <> "-- hoge.cabal - benchmark hoge-bench\n"+                     <> "base ==4.8.0.4,\n"+                     <> "-- quux N/A,\n"+                     <> "parsec ==3.0.2\n"+                     <> "\n"+                   )+    formatResultsCabal input `shouldBe` expected+  it "should show mixed blocks and lines" $ do+    let input = [ simpleResult "lts-5.0" "pack-A" [4,5],+                  simpleResult "lts-5.0" "pack-B" [7,7],+                  cabalResult "lts-5.0" "X.cabal" TargetLibrary [("pack-A", [4,5,1]), ("pack-B", [6,0])],+                  cabalResult "lts-5.0" "X.cabal" (TargetExecutable "X-exe") [("pack-A", []), ("pack-B", [6,6]), ("pack-C", [0,10])],+                  cabalResult "lts-6.6" "Y.cabal" TargetLibrary [("pack-C", [9,99,0])],+                  simpleResult "lts-6.6" "pack-A" [],+                  simpleResult "lts-7.2" "pack-B" [8,4],+                  cabalResult "lts-6.6" "Y.cabal" (TargetTestSuite "X-test") [("pack-D", [1,0]), ("pack-A", [5,0]), ("pack-C", []), ("pack-B", [6,5])]+                ]+        expected = ( "------ lts-5.0\n"+                     <> "pack-A ==4.5,\n"+                     <> "pack-B ==7.7\n"+                     <> "\n"+                     <> "-- X.cabal - library\n"+                     <> "pack-A ==4.5.1,\n"+                     <> "pack-B ==6.0\n"+                     <> "\n"+                     <> "-- X.cabal - executable X-exe\n"+                     <> "-- pack-A N/A,\n"+                     <> "pack-B ==6.6,\n"+                     <> "pack-C ==0.10\n"+                     <> "\n"+                     <> "------ lts-6.6\n"+                     <> "-- Y.cabal - library\n"+                     <> "pack-C ==9.99.0\n"+                     <> "\n"+                     <> "-- pack-A N/A\n"+                     <> "\n"+                     <> "-- Y.cabal - test-suite X-test\n"+                     <> "pack-D ==1.0,\n"+                     <> "pack-A ==5.0,\n"+                     <> "-- pack-C N/A,\n"+                     <> "pack-B ==6.5\n"+                     <> "\n"+                     <> "------ lts-7.2\n"+                     <> "pack-B ==8.4\n"+                     <> "\n"+                   )+    formatResultsCabal input `shouldBe` expected  simpleResult :: Resolver -> PackageName -> [Int] -> Result-simpleResult res name vs = Result { resultIn = SourceStackage res,+simpleResult res name vs = Result { resultIn = SourceStackage res, resultReallyIn = Nothing,                                     resultFor = QueryName name,-                                    resultVersions = Right $ rvers [(name, mversion)]+                                    resultBody = Right $ simpleResultBody name vs                                   }-  where-    mversion = case vs of-      [] -> Nothing-      _ -> Just $ ver vs++cabalResult :: Resolver -> FilePath -> Target -> [(PackageName, [Int])] -> Result+cabalResult res file target vps =+  Result { resultIn = SourceStackage res, resultReallyIn = Nothing,+           resultFor = QueryCabalFile file,+           resultBody = Right $ CabalResultBody file target $ verPairs vps+         }
test/Staversion/Internal/TestUtil.hs view
@@ -1,13 +1,23 @@ module Staversion.Internal.TestUtil-       ( ver, rvers+       ( ver, simpleResultBody,+         verPairs        ) where  import Data.Version (Version(..))-import Staversion.Internal.Query (PackageName, ResultVersions, resultVersionsFromList)+import Staversion.Internal.Query ( PackageName+                                 )+import Staversion.Internal.Result (ResultBody(..))  ver :: [Int] -> Version ver vs = Version vs [] -rvers :: [(PackageName, Maybe Version)] -> ResultVersions-rvers = resultVersionsFromList+verMaybe :: [Int] -> Maybe Version+verMaybe [] = Nothing+verMaybe vs = Just $ ver vs +verPairs :: [(PackageName, [Int])] -> [(PackageName, Maybe Version)]+verPairs = map f where+  f (pname, vs) = (pname, verMaybe vs)++simpleResultBody :: PackageName -> [Int] -> ResultBody+simpleResultBody name vs = SimpleResultBody name $ verMaybe vs
+ test/data/conduit.cabal_test view
@@ -0,0 +1,111 @@+Name:                conduit+Version:             1.2.8+Synopsis:            Streaming data processing library.+description:+    Hackage documentation generation is not reliable. For up to date documentation, please see: <http://www.stackage.org/package/conduit>.+    .+    @conduit@ is a solution to the streaming data problem, allowing for production, transformation, and consumption of streams of data in constant memory. It is an alternative to lazy I\/O which guarantees deterministic resource handling, and fits in the same general solution space as @enumerator@\/@iteratee@ and @pipes@. For a tutorial, please visit <https://www.fpcomplete.com/user/snoyberg/library-documentation/conduit-overview>.+License:             MIT+License-file:        LICENSE+Author:              Michael Snoyman+Maintainer:          michael@snoyman.com+Category:            Data, Conduit+Build-type:          Simple+Cabal-version:       >=1.8+Homepage:            http://github.com/snoyberg/conduit+extra-source-files:  test/main.hs+                   , README.md+                   , ChangeLog.md+                   , fusion-macros.h++Library+  Exposed-modules:     Data.Conduit+                       Data.Conduit.List+                       Data.Conduit.Internal+                       Data.Conduit.Lift+                       Data.Conduit.Internal.Fusion+                       Data.Conduit.Internal.List.Stream+  other-modules:       Data.Conduit.Internal.Pipe+                       Data.Conduit.Internal.Conduit+  Build-depends:       base                     >= 4.5          && < 5+                     , resourcet                >= 1.1          && < 1.2+                     , exceptions               >= 0.6+                     , lifted-base              >= 0.1+                     , transformers-base        >= 0.4.1        && < 0.5+                     , transformers             >= 0.2.2+                     , mtl+                     , mmorph+                     , monad-control+  if !impl(ghc>=7.9)+    build-depends:   void                     >= 0.5.5+  ghc-options:         -Wall+  include-dirs:        .++test-suite test+    hs-source-dirs: test+    main-is: main.hs+    other-modules: Data.Conduit.Extra.ZipConduitSpec+                 , Data.Conduit.StreamSpec+    type: exitcode-stdio-1.0+    cpp-options:   -DTEST+    build-depends:   conduit+                   , base+                   , hspec >= 1.3+                   , QuickCheck >= 2.7+                   , transformers+                   , mtl+                   , resourcet+                   , containers+                   , exceptions >= 0.6+                   , safe+    if !impl(ghc>=7.9)+        build-depends: void+    ghc-options:     -Wall++--test-suite doctests+--    hs-source-dirs: test+--    main-is: doctests.hs+--    type: exitcode-stdio-1.0+--    ghc-options: -threaded+--    build-depends: base, directory, doctest >= 0.8++-- benchmark utf8-memory-usage+--     type: exitcode-stdio-1.0+--     hs-source-dirs: benchmarks+--     build-depends:  base+--                   , text-stream-decode+--                   , bytestring+--                   , text+--                   , conduit+--     main-is:        utf8-memory-usage.hs+--     ghc-options:    -Wall -O2 -with-rtsopts=-s++benchmark optimize-201408+    type: exitcode-stdio-1.0+    hs-source-dirs: benchmarks+    build-depends:  base+                  , conduit+                  , vector+                  , deepseq+                  , containers+                  , transformers+                  , hspec+                  , mwc-random+                  , criterion+                  , kan-extensions+    main-is:        optimize-201408.hs+    ghc-options:    -Wall -O2 -rtsopts++benchmark unfused+    type: exitcode-stdio-1.0+    hs-source-dirs: benchmarks+    build-depends:  base+                  , conduit+                  , criterion+                  , transformers+    main-is:        unfused.hs+    ghc-options:    -Wall -O2 -rtsopts++source-repository head+  type:     git+  location: git://github.com/snoyberg/conduit.git
+ test/data/doctest.cabal_test view
@@ -0,0 +1,118 @@+name:             doctest+version:          0.11.0+synopsis:         Test interactive Haskell examples+description:      The doctest program checks examples in source code comments.+                  It is modeled after doctest for Python+                  (<http://docs.python.org/library/doctest.html>).+                  .+                  Documentation is at+                  <https://github.com/sol/doctest#readme>.+category:         Testing+bug-reports:      https://github.com/sol/doctest/issues+homepage:         https://github.com/sol/doctest#readme+license:          MIT+license-file:     LICENSE+copyright:        (c) 2009-2015 Simon Hengel+author:           Simon Hengel <sol@typeful.net>+maintainer:       Simon Hengel <sol@typeful.net>+build-type:       Simple+cabal-version:    >= 1.8+extra-source-files: example/example.cabal+                  , example/src/Example.hs+                  , example/test/doctests.hs++source-repository head+  type: git+  location: https://github.com/sol/doctest++library+  exposed-modules:+      Test.DocTest+  ghc-options:+      -Wall+  hs-source-dirs:+      src, ghci-wrapper/src+  other-modules:+      Extract+    , GhcUtil+    , Interpreter+    , Location+    , Help+    , PackageDBs+    , Parse+    , Paths_doctest+    , Property+    , Runner+    , Runner.Example+    , Run+    , Util+    , Sandbox+    , Language.Haskell.GhciWrapper+  build-depends:+      base          == 4.*+    , base-compat   >= 0.4.2+    , ghc           >= 7.0 && < 8.2+    , syb           >= 0.3+    , deepseq+    , directory+    , filepath+    , process+    , ghc-paths     >= 0.1.0.9+    , transformers++executable doctest+  main-is:+      Main.hs+  ghc-options:+      -Wall -threaded+  hs-source-dirs:+      driver+  build-depends:+      base          == 4.*+    , doctest++test-suite spec+  main-is:+      Spec.hs+  type:+      exitcode-stdio-1.0+  ghc-options:+      -Wall -threaded+  cpp-options:+      -DTEST+  hs-source-dirs:+      test, src, ghci-wrapper/src+  c-sources:+      test/integration/with-cbits/foo.c+  build-depends:+      base+    , ghc+    , syb+    , deepseq+    , directory+    , filepath+    , process+    , ghc-paths+    , transformers++    , base-compat+    , HUnit+    , hspec         >= 1.5.1+    , QuickCheck    >= 2.8.2+    , stringbuilder >= 0.4+    , silently      >= 1.2.4+    , setenv+    , with-location++test-suite doctests+  main-is:+      doctests.hs+  type:+      exitcode-stdio-1.0+  ghc-options:+      -Wall -threaded+  hs-source-dirs:+      test+  build-depends:+      base+    , doctest
+ test/data/foobar.cabal_test view
@@ -0,0 +1,61 @@+name:                   foobar+version:                0.1.2.0+author:                 Toshio Ito <debug.ito@gmail.com>+maintainer:             Toshio Ito <debug.ito@gmail.com>+license:                BSD3+license-file:           LICENSE+synopsis:               foo+description:            bar+category:               Example+cabal-version:          >= 1.10+build-type:             Simple+-- extra-source-files:     README.md, ChangeLog.md,+                        +library+  default-language:     Haskell2010+  hs-source-dirs:       src+  ghc-options:          -Wall -fno-warn-unused-imports+  default-extensions:   OverloadedStrings+  other-extensions:     CPP, DeriveDataTypeable, TupleSections+  other-modules:        Paths_staversion,+                        Staversion.Internal.HTTP+  build-depends:        base >=4.6 && <4.10,+                        unordered-containers >=0.2.3 && <0.3,++executable foobar+  default-language:     Haskell2010+  hs-source-dirs:       app+  main-is:              Main.hs+  ghc-options:          -Wall -fno-warn-unused-imports -rtsopts -threaded "-with-rtsopts=-N"+  -- other-modules:        +  -- default-extensions:   +  -- other-extensions:     +  -- build-depends:        base, staversion+  -- -- (no build-depends case (quite unlikely, though..))++test-suite spec+  type:                 exitcode-stdio-1.0+  default-language:     Haskell2010+  hs-source-dirs:       test+  ghc-options:          -Wall -fno-warn-unused-imports -fno-warn-orphans "-with-rtsopts=-M512m"+  main-is:              Spec.hs+  default-extensions:   OverloadedStrings+  build-depends:        base, staversion, text, filepath, bytestring,+++flag network-test+  description: Enable network tests.+  default: False++test-suite network-test+  type:                 exitcode-stdio-1.0+  default-language:     Haskell2010+  hs-source-dirs:       test+  ghc-options:          -Wall -fno-warn-unused-imports -fno-warn-orphans "-with-rtsopts=-M512m"+  main-is:              NetworkTest.hs+  default-extensions:   OverloadedStrings+  if !flag(network-test)+    buildable:          False+  -- other-extensions:     +  other-modules:        Staversion.Internal.TestUtil+  build-depends:        base, http-client