Newer
Older
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
import Control.Monad.Trans (liftIO)
import Data.Aeson
import Data.ByteString.Lazy (ByteString)
import Data.List.Split (splitOn)
import qualified Data.Map as Map
import Data.Proxy
import Data.Text.Lazy (pack)
import Data.Text.Lazy.Encoding (encodeUtf8)
import Data.Vector (fromList)
import Network.HTTP.Types
import Network.Wai
import Servant.Swagger
import Servant.Swagger.UI
import API (Mode (..), ParseMode (..), compareSpec)
data ApiReqBody = ApiReqBody
{ sourceA :: String
, sourceB :: String
instance FromJSON ApiReqBody
instance ToJSON ApiReqBody
data ApiResponseType = Equiv | NotEquiv | Undef | ResponseErr
data ApiResponse = ApiResponse
{ responseType :: ApiResponseType
instance ToJSON ApiResponseType
instance ToJSON ApiResponse
instance ToJSON ModelVal where
toJSON (BoolVal b) = toJSON b
toJSON (IntVal n) = toJSON n
toJSON (RealVal n) = toJSON n
toJSON (ManyVal vs) = Array $ fromList $ map toJSON vs
defResp = ApiResponse { responseType = Undef
, model = Nothing
, err = Nothing
, feedback = Nothing
}
:> ReqBody '[JSON] ApiReqBody
:> Post '[JSON] ApiResponse
serverCompare :: Server CompareApi
serverCompare = getCompareResponse
getCompareResponse :: ApiReqBody -> Handler ApiResponse
getCompareResponse ApiReqBody {sourceA = srcA, sourceB = srcB} = do
resp <- liftIO $ compareSpec Release Raw (wrap srcA) (wrap srcB)
return $ case resp of
Equivalent ->
defResp { responseType = Equiv }
NotEquivalent m f ->
defResp { responseType = NotEquiv
, model = Just m
, feedback = Just f
}
defResp { responseType = ResponseErr, err = Just e }
where
wrap s = ( "public class Main {" ++ s ++ "}"
, last $ splitOn " " $ head $ splitOn "(" s
)
toSamples _ = singleSample $ ApiReqBody srcA srcB
where srcA = "public static float real1(float a) {\
\ pre(a >= (2 - 1 + 1));\
\ a += a;\
\ post(a >= (4 - 3 + 3));}"
srcB = "public static float real2(float a) {\
\ pre(a > 2 || a == 2);\
\ a = a * 2;\
\ post(a > 4 || a == 4);}"
instance ToSample ApiResponse where
toSamples _ = singleSample ApiResponse
{ responseType = NotEquiv
, model = Just $ Map.fromList
[ ("a", ManyVal [RealVal 0.0, RealVal (-0.5)])
, ("i", IntVal 10)
]
, feedback = Just $ Feedback (True, True, False, True) (False, True, True, False)
}
docsBS :: ByteString
docsBS = encodeUtf8
. pack
. markdown
$ docsWith opts [intro] mempty (Proxy :: Proxy CompareApi)
where intro = DocIntro "Welcome" ["This is our webservice's API.", "Enjoy!"]
instance ToSchema ApiReqBody
instance ToSchema ApiResponseType
instance ToSchema ApiResponse
instance ToSchema ModelVal where
declareNamedSchema _ = return $ NamedSchema Nothing $
sketchSchema (ManyVal [BoolVal False, IntVal 23, RealVal 7.5])
type SwagApi = SwaggerSchemaUI "api-swagger" "swagger.json"
serverSwagger :: Server SwagApi
serverSwagger = swaggerSchemaUIServer (toSwagger (Proxy :: Proxy CompareApi))
runApi port = runSettings settings app
where settings = ( setPort port
. setBeforeMainLoop (hPutStrLn stderr ("listening on port " ++ show port))
) defaultSettings
type WholeApi = CompareApi :<|> SwagApi :<|> Raw
app :: Application
app = serve (Proxy :: Proxy WholeApi) $
serverCompare :<|> serverSwagger :<|> Tagged serveDocs
where serveDocs _ respond = respond $
responseLBS ok200 [("Content-Type", "text/plain")] docsBS