{-# LANGUAGE DefaultSignatures #-}

module Web.Hyperbole.Effect.QueryData where

import Data.Text (Text, pack)
import Data.Time (UTCTime)
import Data.Word
import Text.Read (readMaybe)
import Web.HttpApiData


-- | Reimplement 'ToHttpApiData' based on Show
class ToQueryData a where
  toQueryData :: a -> Text
  default toQueryData :: (Show a) => a -> Text
  toQueryData = a -> Text
forall a. Show a => a -> Text
showQueryParam


-- | Reimplement 'FromHttpApiData' based on Read
class FromQueryData a where
  parseQueryData :: Text -> Either Text a
  default parseQueryData :: (Read a) => Text -> Either Text a
  parseQueryData = Text -> Either Text a
forall a. Read a => Text -> Either Text a
readQueryParam


instance ToQueryData Int where
  toQueryData :: Int -> Text
toQueryData = Int -> Text
forall a. ToHttpApiData a => a -> Text
toQueryParam
instance FromQueryData Int where
  parseQueryData :: Text -> Either Text Int
parseQueryData = Text -> Either Text Int
forall a. FromHttpApiData a => Text -> Either Text a
parseQueryParam


instance ToQueryData Integer where
  toQueryData :: Integer -> Text
toQueryData = Integer -> Text
forall a. ToHttpApiData a => a -> Text
toQueryParam
instance FromQueryData Integer where
  parseQueryData :: Text -> Either Text Integer
parseQueryData = Text -> Either Text Integer
forall a. FromHttpApiData a => Text -> Either Text a
parseQueryParam


instance ToQueryData Float where
  toQueryData :: Float -> Text
toQueryData = Float -> Text
forall a. ToHttpApiData a => a -> Text
toQueryParam
instance FromQueryData Float where
  parseQueryData :: Text -> Either Text Float
parseQueryData = Text -> Either Text Float
forall a. FromHttpApiData a => Text -> Either Text a
parseQueryParam


instance ToQueryData Double where
  toQueryData :: Double -> Text
toQueryData = Double -> Text
forall a. ToHttpApiData a => a -> Text
toQueryParam
instance FromQueryData Double where
  parseQueryData :: Text -> Either Text Double
parseQueryData = Text -> Either Text Double
forall a. FromHttpApiData a => Text -> Either Text a
parseQueryParam


instance ToQueryData Word where
  toQueryData :: Word -> Text
toQueryData = Word -> Text
forall a. ToHttpApiData a => a -> Text
toQueryParam
instance FromQueryData Word where
  parseQueryData :: Text -> Either Text Word
parseQueryData = Text -> Either Text Word
forall a. FromHttpApiData a => Text -> Either Text a
parseQueryParam


instance ToQueryData Word8 where
  toQueryData :: Word8 -> Text
toQueryData = Word8 -> Text
forall a. ToHttpApiData a => a -> Text
toQueryParam
instance FromQueryData Word8 where
  parseQueryData :: Text -> Either Text Word8
parseQueryData = Text -> Either Text Word8
forall a. FromHttpApiData a => Text -> Either Text a
parseQueryParam


instance ToQueryData Word16 where
  toQueryData :: Word16 -> Text
toQueryData = Word16 -> Text
forall a. ToHttpApiData a => a -> Text
toQueryParam
instance FromQueryData Word16 where
  parseQueryData :: Text -> Either Text Word16
parseQueryData = Text -> Either Text Word16
forall a. FromHttpApiData a => Text -> Either Text a
parseQueryParam


instance ToQueryData Word32 where
  toQueryData :: Word32 -> Text
toQueryData = Word32 -> Text
forall a. ToHttpApiData a => a -> Text
toQueryParam
instance FromQueryData Word32 where
  parseQueryData :: Text -> Either Text Word32
parseQueryData = Text -> Either Text Word32
forall a. FromHttpApiData a => Text -> Either Text a
parseQueryParam


instance ToQueryData Word64 where
  toQueryData :: Word64 -> Text
toQueryData = Word64 -> Text
forall a. ToHttpApiData a => a -> Text
toQueryParam
instance FromQueryData Word64 where
  parseQueryData :: Text -> Either Text Word64
parseQueryData = Text -> Either Text Word64
forall a. FromHttpApiData a => Text -> Either Text a
parseQueryParam


instance ToQueryData Bool where
  toQueryData :: Bool -> Text
toQueryData = Bool -> Text
forall a. ToHttpApiData a => a -> Text
toQueryParam
instance FromQueryData Bool where
  parseQueryData :: Text -> Either Text Bool
parseQueryData = Text -> Either Text Bool
forall a. FromHttpApiData a => Text -> Either Text a
parseQueryParam


instance ToQueryData Text where
  toQueryData :: Text -> Text
toQueryData = Text -> Text
forall a. ToHttpApiData a => a -> Text
toQueryParam
instance FromQueryData Text where
  parseQueryData :: Text -> Either Text Text
parseQueryData = Text -> Either Text Text
forall a. FromHttpApiData a => Text -> Either Text a
parseQueryParam


instance ToQueryData Char where
  toQueryData :: Char -> Text
toQueryData = Char -> Text
forall a. ToHttpApiData a => a -> Text
toQueryParam
instance FromQueryData Char where
  parseQueryData :: Text -> Either Text Char
parseQueryData = Text -> Either Text Char
forall a. FromHttpApiData a => Text -> Either Text a
parseQueryParam


instance ToQueryData UTCTime where
  toQueryData :: UTCTime -> Text
toQueryData = UTCTime -> Text
forall a. ToHttpApiData a => a -> Text
toQueryParam
instance FromQueryData UTCTime where
  parseQueryData :: Text -> Either Text UTCTime
parseQueryData = Text -> Either Text UTCTime
forall a. FromHttpApiData a => Text -> Either Text a
parseQueryParam


instance (Show a) => ToQueryData [a] where
  toQueryData :: [a] -> Text
toQueryData = [a] -> Text
forall a. Show a => a -> Text
showQueryParam
instance (Read a) => FromQueryData [a] where
  parseQueryData :: Text -> Either Text [a]
parseQueryData = Text -> Either Text [a]
forall a. Read a => Text -> Either Text a
readQueryParam


instance (ToQueryData a) => ToQueryData (Maybe a) where
  toQueryData :: Maybe a -> Text
toQueryData Maybe a
Nothing = Text
""
  toQueryData (Just a
a) = a -> Text
forall a. ToQueryData a => a -> Text
toQueryData a
a
instance (Read a) => FromQueryData (Maybe a) where
  parseQueryData :: Text -> Either Text (Maybe a)
parseQueryData Text
"" = Maybe a -> Either Text (Maybe a)
forall a. a -> Either Text a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe a
forall a. Maybe a
Nothing
  parseQueryData Text
t = Text -> Either Text (Maybe a)
forall a. Read a => Text -> Either Text a
readQueryParam Text
t


instance (ToQueryData a, ToQueryData b) => ToQueryData (Either a b) where
  toQueryData :: Either a b -> Text
toQueryData (Left a
a) = a -> Text
forall a. ToQueryData a => a -> Text
toQueryData a
a
  toQueryData (Right b
b) = b -> Text
forall a. ToQueryData a => a -> Text
toQueryData b
b
instance (FromQueryData a, FromQueryData b) => FromQueryData (Either a b) where
  parseQueryData :: Text -> Either Text (Either a b)
parseQueryData Text
t =
    case forall a. FromQueryData a => Text -> Either Text a
parseQueryData @a Text
t of
      Right a
a -> Either a b -> Either Text (Either a b)
forall a. a -> Either Text a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either a b -> Either Text (Either a b))
-> Either a b -> Either Text (Either a b)
forall a b. (a -> b) -> a -> b
$ a -> Either a b
forall a b. a -> Either a b
Left a
a
      Left Text
_ -> do
        case forall a. FromQueryData a => Text -> Either Text a
parseQueryData @b Text
t of
          Left Text
_ -> Text -> Either Text (Either a b)
forall a b. a -> Either a b
Left (Text -> Either Text (Either a b))
-> Text -> Either Text (Either a b)
forall a b. (a -> b) -> a -> b
$ Text
"Could not parseQueryData Either: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
t
          Right b
b -> Either a b -> Either Text (Either a b)
forall a. a -> Either Text a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either a b -> Either Text (Either a b))
-> Either a b -> Either Text (Either a b)
forall a b. (a -> b) -> a -> b
$ b -> Either a b
forall a b. b -> Either a b
Right b
b


-- | Encode a Show as a query param
showQueryParam :: (Show a) => a -> Text
showQueryParam :: forall a. Show a => a -> Text
showQueryParam a
a = String -> Text
forall a. ToHttpApiData a => a -> Text
toQueryParam (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ a -> String
forall a. Show a => a -> String
show a
a


-- | Decode a Read as a query param
readQueryParam :: (Read a) => Text -> Either Text a
readQueryParam :: forall a. Read a => Text -> Either Text a
readQueryParam Text
t = do
  String
str <- Text -> Either Text String
forall a. FromHttpApiData a => Text -> Either Text a
parseQueryParam Text
t
  case String -> Maybe a
forall a. Read a => String -> Maybe a
readMaybe String
str of
    Maybe a
Nothing -> Text -> Either Text a
forall a b. a -> Either a b
Left (Text -> Either Text a) -> Text -> Either Text a
forall a b. (a -> b) -> a -> b
$ String -> Text
pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ String
"Could not read query param: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
str
    Just a
a -> a -> Either Text a
forall a. a -> Either Text a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
a


parseQueryDatas :: (Traversable t, FromQueryData a) => t Text -> Either Text (t a)
parseQueryDatas :: forall (t :: * -> *) a.
(Traversable t, FromQueryData a) =>
t Text -> Either Text (t a)
parseQueryDatas = (Text -> Either Text a) -> t Text -> Either Text (t a)
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> t a -> f (t b)
traverse Text -> Either Text a
forall a. FromQueryData a => Text -> Either Text a
parseQueryData