module Staversion.Internal.Exec
( main,
processCommand,
_processCommandWithCustomBuildPlanManager
) where
import Control.Applicative ((<$>))
import Control.Monad (mapM_, when)
import Control.Monad.IO.Class (liftIO)
import Data.Either (rights)
import Data.Function (on)
import Data.List (groupBy, nub)
import Data.Maybe (isJust)
import Data.Text (unpack, pack)
import qualified Data.Text.Lazy.IO as TLIO
import Staversion.Internal.Aggregate (aggregateResults)
import Staversion.Internal.BuildPlan
( BuildPlan, packageVersion, buildPlanSource,
newBuildPlanManager, loadBuildPlan,
BuildPlanManager, manStackConfig
)
import Staversion.Internal.Command
( parseCommandArgs,
Command(..)
)
import Staversion.Internal.EIO (EIO, runEIO, toEIO)
import Staversion.Internal.Format (formatAggregatedResults)
import qualified Staversion.Internal.Format as Format
import Staversion.Internal.Log (logDebug, logError, logWarn, Logger, putLogEntry)
import Staversion.Internal.Query
( Query(..), PackageSource(..), PackageName, ErrorMsg
)
import Staversion.Internal.Result
( Result(..), ResultBody, ResultBody'(..), ResultSource(..),
singletonResult
)
import Staversion.Internal.Cabal (BuildDepends(..), loadCabalFile)
import Staversion.Internal.StackConfig
( StackConfig, newStackConfig, scCommand, readProjectCabals
)
main :: IO ()
main = do
comm <- parseCommandArgs
formatAndShow comm =<< aggregate comm =<< (processCommand comm)
where
aggregate comm results = case commAggregator comm of
Nothing -> return $ map singletonResult results
Just agg -> do
logDebug (commLogger comm) ("Results before aggregation: " ++ show results)
let (aresults, logs) = aggregateResults agg results
mapM_ (putLogEntry $ commLogger comm) logs
return aresults
formatAndShow comm aresults = do
TLIO.putStr $ formatAggregatedResults (commFormatConfig comm) aresults
data ResolvedQuery = RQueryOne PackageName
| RQueryCabal FilePath BuildDepends
deriving (Show, Eq, Ord)
processCommand :: Command -> IO [Result]
processCommand = _processCommandWithCustomBuildPlanManager return
stackConfigFromCommand :: Command -> StackConfig
stackConfigFromCommand comm = (newStackConfig $ commLogger comm) { scCommand = commStackCommand comm }
makeBuildPlanManager :: Command -> IO BuildPlanManager
makeBuildPlanManager comm = do
man <- newBuildPlanManager (commBuildPlanDir comm) (commLogger comm) (commAllowNetwork comm)
return $ man { manStackConfig = stackConfigFromCommand comm }
_processCommandWithCustomBuildPlanManager :: (BuildPlanManager -> IO BuildPlanManager) -> Command -> IO [Result]
_processCommandWithCustomBuildPlanManager customBPM comm = impl where
impl = do
bp_man <- customBPM =<< makeBuildPlanManager comm
query_pairs <- resolveQueries' logger stack_conf $ commQueries comm
results <- fmap concat $ mapM (processQueriesIn bp_man query_pairs) $ commSources comm
warnEmpty results
return results
logger = commLogger comm
stack_conf = stackConfigFromCommand comm
processQueriesIn bp_man query_pairs source = do
let queried_names = nub $ concat $ map (getQueriedPackageNames) $ rights $ map snd $ query_pairs
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) $ query_pairs
makeResult source e_build_plan (orig_query, e_rquery) = case (e_build_plan, e_rquery) of
(Left error_msg, _) -> resultForBody $ Left error_msg
(Right _, Left error_msg) -> resultForBody $ Left error_msg
(Right build_plan, Right rquery) -> resultForBody $ Right $ searchVersions build_plan rquery
where
resultForBody body =
Result { resultIn = ResultSource { resultSourceQueried = source,
resultSourceReal = realSource e_build_plan
},
resultFor = orig_query,
resultBody = body
}
logBuildPlanResult (Right _) = logDebug logger ("Successfully retrieved build plan.")
logBuildPlanResult (Left error_msg) = logError logger ("Failed to load build plan: " ++ error_msg)
warnEmpty [] = logWarn logger "Got no result. Try --help option."
warnEmpty _ = return ()
realSource :: Either e BuildPlan -> Maybe PackageSource
realSource (Left _) = Nothing
realSource (Right bp) = Just $ buildPlanSource bp
resolveQueries' :: Logger -> StackConfig -> [Query] -> IO [(Query, Either ErrorMsg ResolvedQuery)]
resolveQueries' logger sconf queries = fmap concat $ mapM resolveToList queries where
resolveToList query = do
eret <- resolveQuery logger sconf query
case eret of
Right rqueries -> return $ map (\rq -> (query, Right rq)) rqueries
Left err -> return $ [(query, Left err)]
resolveQuery :: Logger -> StackConfig -> Query -> IO (Either ErrorMsg [ResolvedQuery])
resolveQuery logger sconf query = reportError =<< (runEIO $ resolveQueryEIO logger sconf query)
where
reportError eret = do
case eret of
Left err -> logError logger err
Right _ -> return ()
return eret
resolveQueryEIO :: Logger -> StackConfig -> Query -> EIO [ResolvedQuery]
resolveQueryEIO logger sconf query =
case query of
QueryName name -> return $ [RQueryOne name]
QueryCabalFile file -> doCabalFile file
QueryStackYaml file -> doStackYaml (Just file)
QueryStackYamlDefault -> doStackYaml Nothing
where
doCabalFile file = do
liftIO $ logDebug logger ("Load " ++ file ++ " for build-depends fields.")
fmap (processBuildDependsList file) $ toEIO $ loadCabalFile file
processBuildDependsList file = map (RQueryCabal file) . filter ((0 <) . length . depsPackages)
doStackYaml mstack_yaml = fmap concat $ mapM recurse =<< (toEIO $ readProjectCabals sconf mstack_yaml)
where
recurse f = resolveQueryEIO logger sconf (QueryCabalFile f)
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