content: {-# LANGUAGE CPP #-}
  {-# LANGUAGE OverloadedStrings #-}
  module Argon.Results (order, filterResults, filterNulls, exportStream)
      where
  
  import Data.Ord (comparing)
  import Data.List (sortBy)
  import Data.String (IsString)
  #if __GLASGOW_HASKELL__ < 710
  import Control.Applicative ((<*), (*>))
  #endif
  
  import Data.Aeson (encode)
  import Pipes
  import Pipes.Group
  import qualified Pipes.Prelude as P
  import qualified Pipes.ByteString as PB
  import Lens.Simple ((^.))
  
  import Argon.Formatters
  import Argon.Types
  
  
  -- sortOn is built-in only in base 4.8.0.0 onwards
  sortOn :: Ord b => (a -> b) -> [a] -> [a]
  sortOn f =
    map snd . sortBy (comparing fst) . map (\x -> let y = f x in y `seq` (y, x))
  
  -- | Order a list of blocks. Ordering is done with respect to:
  --
  --     1. complexity (descending)
  --     2. line number (ascending)
  --     3. function name (alphabetically)
  order :: [ComplexityBlock] -> [ComplexityBlock]
  order = sortOn (\(CC ((l, _), f, cc)) -> (-cc, l, f))
  
  -- | A result is discarded if it correspond to a successful analysis and there
  --   are no blocks to show
  filterNulls :: (FilePath, AnalysisResult) -> Bool
  filterNulls (_, r) = case r of
                         Left  _  -> True
                         Right [] -> False
                         _        -> True
  
  -- | Filter the results of the analysis, with respect to the given
  --   'Config'.
  filterResults :: Config
                -> (FilePath, AnalysisResult)
                -> (FilePath, AnalysisResult)
  filterResults _ (s, Left err) = (s, Left err)
  filterResults o (s, Right rs) =
      (s, Right $ order [r | r@(CC (_, _, cc)) <- rs, cc >= minCC o])
  
  -- | Export analysis' results. How to export the data is defined by the
  --   'Config' parameter.
  exportStream :: (MonadIO m)
               => Config
               -> Producer (FilePath, AnalysisResult) m ()
               -> Effect m ()
  exportStream conf source =
      case outputMode conf of
        BareText -> source >-> bareTextFormatter >-> P.stdoutLn
        Colored  -> source >-> coloredTextFormatter >-> P.stdoutLn
        JSON     -> jsonStream (source >-> P.map encode)
                    >-> for cat (\i -> PB.fromLazy i >-> PB.stdout)
  
  jsonStream :: (MonadIO m)
             => IsString a
             => Producer a m ()
             -> Producer a m ()
  jsonStream source = yield "[" *> intersperse' "," source <* yield "]\n"
  
  intersperse' :: Monad m => a -> Producer a m r -> Producer a m r
  intersperse' a producer = intercalates (yield a) (producer ^. chunksOf 1)
  