packages feed

gipeda-0.2: src/BenchmarkSettings.hs

{-# LANGUAGE OverloadedStrings, DeriveGeneric #-}
module BenchmarkSettings where

import Data.Yaml
import Data.Aeson
import qualified Data.Vector as V
import Data.Functor
import Control.Applicative
import Control.Monad
import Data.Monoid
import Data.Maybe
import GHC.Generics (Generic)


data NumberType = IntegralNT | SmallIntegralNT | FloatingNT
    deriving (Show)

instance ToJSON NumberType where
    toJSON IntegralNT =      String "integral"
    toJSON SmallIntegralNT = String "small integral"
    toJSON FloatingNT =      String "floating"

type BenchName = String
data BenchSettings = BenchSettings
    { smallerIsBetter :: Bool
    , unit :: String
    , numberType :: NumberType
    , group :: String
    , threshold :: Double
    , important :: Bool
    }
    deriving (Show, Generic)
instance ToJSON BenchSettings

defaultBenchSettings :: BenchSettings
defaultBenchSettings = BenchSettings True "" IntegralNT "" 3 True

newtype S = S { unS :: BenchName -> BenchSettings }
newtype SM = SM (BenchName -> (BenchSettings -> BenchSettings))

instance Monoid SM where
    mempty = SM (const id)
    mappend (SM f) (SM g) = SM (\n -> g n . f n)

instance FromJSON NumberType where
    parseJSON = withText "type" $ \t -> case t of
        "small integral" -> return SmallIntegralNT
        "integral" -> return IntegralNT
        "float" -> return FloatingNT
        v -> fail $ "Unknown \"type\": " ++ show v

-- Very naive glob, * only at the end
matches :: String -> String -> Bool
matches [] [] = True
matches _ ('*':[]) = True
matches (x:xs) (m:ms) = x == m && matches xs ms

instance FromJSON SM where
    parseJSON (Object o) = do
        m <- o .: "match"
        mt <- o .:? "type"
        mu <- o .:? "unit"
        mg <- o .:? "group"
        ms <- o .:? "smallerIsBetter"
        mth <- o .:? "threshold"
        mi <- o .:? "important"
        return $ SM $ \n b ->
            if n `matches` m then
               b { numberType      = fromMaybe (numberType b) mt
                 , unit            = fromMaybe (unit b) mu
                 , group           = fromMaybe (group b) mg
                 , smallerIsBetter = fromMaybe (smallerIsBetter b) ms
                 , threshold       = fromMaybe (threshold b) mth
                 , important       = fromMaybe (important b) mi
                 }
            else b
    parseJSON _ = mzero

instance FromJSON S where
    parseJSON (Array a) = do
        mods <- mapM parseJSON (V.toList a)
        let SM sm = mconcat mods
        return $ S $ \n -> sm n defaultBenchSettings

data Settings = Settings
   { title :: String
   , revisionInfo :: String
   , diffLink :: Maybe String
   , limitRecent :: Integer
   , start :: String
   , interestingTags :: Maybe String
   , interestingBranches :: Maybe String
   , benchSettings :: BenchName -> BenchSettings
   }

instance FromJSON Settings where
    parseJSON (Object v) =
        Settings <$> v .: "title"
                 <*> v .: "revisionInfo"
                 <*> v .: "diffLink"
                 <*> v .: "limitRecent"
                 <*> v .: "start"
                 <*> v .:? "interestingTags"
                 <*> v .:? "interestingBranches"
                 <*> (unS <$> v.: "benchmarks")
    parseJSON _ = mzero


readSettings :: FilePath -> IO Settings
readSettings fname = either (error.show) id <$> decodeFileEither fname