{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeOperators #-} module Servant.PureScript ( HasBridge , languageBridge , defaultBridge , defaultBridgeProxy , DefaultBridge , writeAPIModule , writeAPIModuleWithSettings , Settings (..) , apiModuleName , readerParams , standardImports , defaultSettings , addReaderParam , jsonParseUrlPiece , jsonToUrlPiece , jsonParseHeader , jsonToHeader ) where import Data.Aeson import Data.Bifunctor import Data.ByteString (ByteString) import qualified Data.ByteString.Lazy as BS import Data.Monoid import Data.Proxy import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Encoding as T import Language.PureScript.Bridge import Network.HTTP.Types (urlDecode, urlEncode) import Servant.Foreign import Servant.PureScript.CodeGen import Servant.PureScript.Internal import System.Directory import System.FilePath import System.IO (IOMode (..), withFile) import Text.PrettyPrint.Mainland (hPutDocLn) writeAPIModule :: forall bridgeSelector api. ( HasForeign (PureScript bridgeSelector) PSType api , GenerateList PSType (Foreign PSType api) , HasBridge bridgeSelector ) => FilePath -> Proxy bridgeSelector -> Proxy api -> IO () writeAPIModule = writeAPIModuleWithSettings defaultSettings writeAPIModuleWithSettings :: forall bridgeSelector api. ( HasForeign (PureScript bridgeSelector) PSType api , GenerateList PSType (Foreign PSType api) , HasBridge bridgeSelector ) => Settings -> FilePath -> Proxy bridgeSelector -> Proxy api -> IO () writeAPIModuleWithSettings opts root pBr pAPI = do let apiList = apiToList pAPI pBr let contents = genModule opts apiList unlessM (doesDirectoryExist mDir) $ createDirectoryIfMissing True mDir withFile mPath WriteMode $ flip hPutDocLn contents where mFile = (joinPath . map T.unpack . T.splitOn "." $ _apiModuleName opts) <> ".purs" mPath = root mFile mDir = takeDirectory mPath -- | Use this function for implementing 'parseUrlPiece' in your FromHttpApiData instances -- in order to be compatible with the generated PS code. -- -- > -- > instance ToHttpApiData MyDataType where -- > toUrlPiece = jsonToUrlPiece -- > toHeader = jsonToHeader -- > -- > instance FromHttpApiData MyDataType where -- > parseUrlPiece = jsonParseUrlPiece -- > parseHeader = jsonParseHeader -- > -- jsonParseUrlPiece :: FromJSON a => Text -> Either Text a jsonParseUrlPiece = first T.pack . eitherDecodeStrict . T.encodeUtf8 -- | Use this function for implementing 'toUrlPiece' in your ToHttpApiData instances -- in order to be compatible with the generated PS code. jsonToUrlPiece :: ToJSON a => a -> Text jsonToUrlPiece = T.decodeUtf8 . jsonToHeader -- | Use this function for implementing 'parseHeader' in your FromHttpApiData instances -- in order to be compatible with the generated PS code. jsonParseHeader :: FromJSON a => ByteString -> Either Text a jsonParseHeader = first T.pack . eitherDecodeStrict . urlDecode True -- | Use this function for implementing 'toHeader' in your ToHttpApiData instances -- in order to be compatible with the generated PS code. jsonToHeader :: ToJSON a => a -> ByteString jsonToHeader = urlEncode True . BS.toStrict . encode