packages feed

festung-0.9.1.1: src/Festung/Frontend/Converters.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NamedFieldPuns #-}

module Festung.Frontend.Converters
  ( vaultObjectParser
  , parametersParser
  , queryParser
  , vaultObjectEncoder
  , rowEncoder
  , headerEncoder
  , resultEncoder
  , errorObj
  ) where

import           Data.Aeson
import           Data.Aeson.Types
import           Data.Maybe
import           Data.Scientific
import qualified Data.Text                  as T
import qualified Data.Vector                as V
import qualified Festung.Vault.Persistence  as P


-- | Backport of Data.Aeson.Encoding:array from aeson 1.0.0.0
array :: [Value] -> Value
array = Array . V.fromList


-- | Converts a json object to a sqlcipher value
vaultObjectParser :: Value -> Parser P.Value
vaultObjectParser (String s) = return $ P.StringValue (T.unpack s)
vaultObjectParser (Number n) = return $ either P.FloatValue P.IntValue $ floatingOrInteger n
vaultObjectParser Null       = return P.NullValue
vaultObjectParser _          = fail "Can't convert to a SQLCipher type"


parametersParser :: Value -> Parser [P.Value]
parametersParser (Array a)  = mapM vaultObjectParser (V.toList a)
parametersParser (Object _) = fail "Not implemented yet"
parametersParser _          = fail "Parameters must be an array, or a mapping of parameters"


queryParser :: Value -> Parser (String, [P.Value])
queryParser = withObject "Should be a query object" $ \ obj -> do
    query       <- obj .: "sql"
    paramsArray <- obj .:? "params" .!= emptyArray
    params      <- parametersParser paramsArray
    return (query, params)


vaultObjectEncoder :: P.Value -> Value
vaultObjectEncoder (P.IntValue i)    = Number (scientific (fromIntegral i) 0)
vaultObjectEncoder (P.StringValue s) = String (T.pack s)
vaultObjectEncoder (P.FloatValue f)  = Number (fromFloatDigits f)
vaultObjectEncoder P.NullValue       = Null


rowEncoder :: [P.Value] -> Value
rowEncoder = array . map vaultObjectEncoder


headerEncoder :: [P.Header] -> Value
headerEncoder = array <$> map go
    where go (columnName, columnType) =
              let columnType' = fromMaybe "dynamic" columnType in
              object [ "name" .= String (T.pack columnName )
                     , "type" .= String (T.pack columnType')
                     ]


resultEncoder :: P.QueryResult -> Value
resultEncoder P.QueryResult{P.rows, P.lastRowId, P.headers, P.rowsChanged} =
    let jsonHeaders = headerEncoder headers
        jsonRows    = array $ map rowEncoder rows
    in object [ "headers"      .= jsonHeaders
              , "data"         .= jsonRows
              , "last_row_id"  .= lastRowId
              , "rows_changed" .= fromMaybe (-1) rowsChanged
              ]


errorObj :: String -> String -> Value
errorObj t d = object [ "error" .= object [ "type" .= t, "description" .= d ] ]