{-|
Module      : SimFin.Types.StringFrac
Description : Wrapper for parsing JSON strings or numbers as a RealFrac.
Copyright   : (c) Owen Shepherd, 2022
License     : MIT
Maintainer  : owen@owen.cafe
-}

module SimFin.Types.StringFrac
  ( StringFrac(..)
  ) where

import Data.Aeson hiding (String)
import qualified Data.Aeson as A
import Data.Aeson.Types (typeMismatch)
import qualified Data.Text as T
import Text.Read (readEither)

-- | Wrapper that parses the string '"1.23"' and the number '1.23' the same.
-- Uses the read instance for the 'String', and 'realToFrac' ('Data.Scientific.Scientific' -> a)
-- for the number.

newtype StringFrac a = StringFrac { StringFrac a -> a
unStringFrac :: a }
  deriving Int -> StringFrac a -> ShowS
[StringFrac a] -> ShowS
StringFrac a -> String
(Int -> StringFrac a -> ShowS)
-> (StringFrac a -> String)
-> ([StringFrac a] -> ShowS)
-> Show (StringFrac a)
forall a. Show a => Int -> StringFrac a -> ShowS
forall a. Show a => [StringFrac a] -> ShowS
forall a. Show a => StringFrac a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [StringFrac a] -> ShowS
$cshowList :: forall a. Show a => [StringFrac a] -> ShowS
show :: StringFrac a -> String
$cshow :: forall a. Show a => StringFrac a -> String
showsPrec :: Int -> StringFrac a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> StringFrac a -> ShowS
Show

instance (Read a, RealFrac a) => FromJSON (StringFrac a) where
  parseJSON :: Value -> Parser (StringFrac a)
parseJSON (A.String Text
s) = case String -> Either String a
forall a. Read a => String -> Either String a
readEither (String -> Either String a) -> String -> Either String a
forall a b. (a -> b) -> a -> b
$ Text -> String
T.unpack Text
s of
    Left String
err -> String -> Parser (StringFrac a)
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
err
    Right a
a -> StringFrac a -> Parser (StringFrac a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (StringFrac a -> Parser (StringFrac a))
-> StringFrac a -> Parser (StringFrac a)
forall a b. (a -> b) -> a -> b
$ a -> StringFrac a
forall a. a -> StringFrac a
StringFrac a
a
  parseJSON (Number Scientific
n) = StringFrac a -> Parser (StringFrac a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (StringFrac a -> Parser (StringFrac a))
-> StringFrac a -> Parser (StringFrac a)
forall a b. (a -> b) -> a -> b
$ a -> StringFrac a
forall a. a -> StringFrac a
StringFrac (a -> StringFrac a) -> a -> StringFrac a
forall a b. (a -> b) -> a -> b
$ Scientific -> a
forall a b. (Real a, Fractional b) => a -> b
realToFrac Scientific
n
  parseJSON Value
v = String -> Value -> Parser (StringFrac a)
forall a. String -> Value -> Parser a
typeMismatch String
"Number or String" Value
v