packages feed

flashblast-0.0.4.0: src/FlashBlast/ForvoClient.hs

{-# LANGUAGE TemplateHaskell #-}
module FlashBlast.ForvoClient where

import Data.Aeson
import Polysemy
import Polysemy.Error
import Polysemy.Input
import RIO hiding (fromException)
import qualified RIO.Text as T
import Network.HTTP.Simple

newtype Locale = Locale Text
  deriving (Eq, Show, Generic, Ord)

instance ToJSON Locale
instance FromJSON Locale

newtype ForvoStandardPronunciationResponseBody = ForvoStandardPronunciationResponseBody {
  items :: [ForvoPronunciationJson]
} deriving (Eq, Show, Generic)

instance FromJSON ForvoStandardPronunciationResponseBody
instance ToJSON ForvoStandardPronunciationResponseBody

data ForvoPronunciationJson = ForvoPronunciationJson {
  id :: Int
, word :: Text
, original :: Text
, hits :: Int
, username:: Text
, sex:: Text
, country:: Text
, code :: Text
, langname :: Text
, pathmp3 ::Text
, pathogg :: Text
, rate:: Int
, num_votes :: Int
, num_positive_votes:: Int
} deriving (Eq, Show, Generic)

instance FromJSON ForvoPronunciationJson
instance ToJSON ForvoPronunciationJson

newtype MP3Url = MP3Url Text
  deriving (Eq, Show, Generic)

class HasMP3Url x where
  mp3Url :: Lens' x MP3Url

instance HasMP3Url ForvoPronunciationJson where
  mp3Url = lens (MP3Url . pathmp3) undefined

data ForvoClient m a where
  StandardPronunciation :: Locale -> Text -> ForvoClient m ForvoStandardPronunciationResponseBody
  MP3For :: HasMP3Url x => x -> ForvoClient m ByteString

makeSem ''ForvoClient

newtype ForvoAPIKey = ForvoAPIKey Text
  deriving (Eq, Show, Generic)

data RemoteHttpRequest m a where
  RequestJSON :: FromJSON a => Text -> RemoteHttpRequest m a
  RequestBS   :: Text -> RemoteHttpRequest m ByteString

makeSem ''RemoteHttpRequest

interpretRemoteHttpRequest :: Members '[Embed IO, Error JSONException, Error SomeException] r => Sem (RemoteHttpRequest ': r) a -> Sem r a
interpretRemoteHttpRequest = interpret \case
  RequestJSON x -> do
    let k = parseRequest $ T.unpack x
    case k of
      Left e -> throw @SomeException e
      Right x' -> do
        j <- fromException @JSONException $ httpJSON x'
        return $ getResponseBody j
  RequestBS x -> do
    let k = parseRequest $ T.unpack x
    case k of
      Left e -> throw @SomeException e
      Right x' -> do
        j <- fromException @JSONException $ httpBS x'
        return $ getResponseBody j

interpretForvoClient :: Members '[RemoteHttpRequest, Input ForvoAPIKey] r => Sem (ForvoClient ': r) a -> Sem r a
interpretForvoClient = interpret \case
  StandardPronunciation (Locale l) t -> do
    ForvoAPIKey f <- input @ForvoAPIKey
    let k = "https://apifree.forvo.com/key/" <> f <> "/format/json/action/standard-pronunciation/word/" <> t <> "/language/" <> l
    requestJSON k
  MP3For x -> let (MP3Url x') = view mp3Url x
              in requestBS x'