{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-orphans #-}
-- | This module only exports orphan 'Ae.FromJSON' and 'Ae.ToJSON' instances.
-- Import as:
--
-- @
-- import "Money.Aeson" ()
-- @
module Money.Aeson () where
import Control.Applicative ((<|>), empty)
import Control.Monad ((<=<), when)
import qualified Data.Aeson as Ae
import Data.Ratio ((%), numerator, denominator)
import qualified Data.Text as T
import GHC.TypeLits (KnownSymbol)
import qualified Money
import qualified Money.Internal as MoneyI
--------------------------------------------------------------------------------
-- | Compatible with 'Money.SomeDense'
--
-- Example rendering @'Money.dense'' (2 '%' 3) :: 'Money.Dense' \"BTC\"@:
--
-- @
-- [\"BTC\", 2, 3]
-- @
--
-- Note: The JSON serialization changed in version 0.4 (the leading @"Dense"@
-- string was dropped from the rendered 'Ae.Array').
instance KnownSymbol currency => Ae.ToJSON (Money.Dense currency) where
toJSON = Ae.toJSON . Money.toSomeDense
-- | Compatible with 'Money.SomeDense'
--
-- Note: The JSON serialization changed in @safe-money@ version 0.4. However,
-- this instance is still able to cope with the previous format.
instance KnownSymbol currency => Ae.FromJSON (Money.Dense currency) where
parseJSON = maybe empty pure <=< fmap Money.fromSomeDense . Ae.parseJSON
-- | Compatible with 'Money.Dense'
--
-- Note: The JSON serialization changed in @safe-money@ version 0.4 (the leading
-- @"Dense"@ string was dropped from the rendered 'Ae.Array').
instance Ae.ToJSON Money.SomeDense where
toJSON = \sd ->
let r = Money.someDenseAmount sd
in Ae.toJSON (MoneyI.someDenseCurrency' sd, numerator r, denominator r)
-- | Compatible with 'Money.Dense'.
--
-- Note: The JSON serialization changed in @safe-money@ version 0.4. However,
-- this instance is still able to cope with the previous format.
instance Ae.FromJSON Money.SomeDense where
parseJSON = \v -> do
(c, n, d) <- Ae.parseJSON v <|> do
-- Pre 0.4 format.
("Dense" :: String, c, n, d) <- Ae.parseJSON v
pure (c, n, d)
when (d == 0) (fail "denominator is zero")
maybe empty pure (MoneyI.mkSomeDense' c (n % d))
-- | Compatible with 'Money.SomeDiscrete'
--
-- Example rendering @'Money.discrete' 43 :: 'Money.Discrete' \"BTC\" \"satoshi\"@:
--
-- @
-- [\"BTC\", 100000000, 1, 43]
-- @
--
-- Note: The JSON serialization changed in @safe-money@ version 0.4 (the leading
-- @"Discrete"@ string was dropped from the rendered 'Ae.Array').
instance
( KnownSymbol currency, Money.GoodScale scale
) => Ae.ToJSON (Money.Discrete' currency scale) where
toJSON = Ae.toJSON . Money.toSomeDiscrete
-- | Compatible with 'Money.SomeDiscrete'
--
-- Note: The JSON serialization changed in @safe-money@ version 0.4. However,
-- this instance is still able to cope with the previous format.
instance
( KnownSymbol currency, Money.GoodScale scale
) => Ae.FromJSON (Money.Discrete' currency scale) where
parseJSON = maybe empty pure <=< fmap Money.fromSomeDiscrete . Ae.parseJSON
-- | Compatible with 'Money.Discrete''
--
-- Note: The JSON serialization changed in version 0.4 (the leading @"Discrete"@
-- string was dropped from the rendered 'Ae.Array').
instance Ae.ToJSON Money.SomeDiscrete where
toJSON = \sd ->
let r = Money.someDiscreteScale sd
in Ae.toJSON (MoneyI.someDiscreteCurrency' sd,
numerator r, denominator r,
Money.someDiscreteAmount sd)
-- | Compatible with 'Money.Discrete''
--
-- Note: The JSON serialization changed in version 0.4. However, this instance
-- is still able to cope with the previous format.
instance Ae.FromJSON Money.SomeDiscrete where
parseJSON = \v -> do
(c, n, d, a) <- Ae.parseJSON v <|> do
-- Pre 0.4 format.
("Discrete" :: T.Text, c, n, d, a) <- Ae.parseJSON v
pure (c, n, d, a)
when (d == 0) (fail "denominator is zero")
maybe empty pure (MoneyI.mkSomeDiscrete' c (n % d) a)
-- | Compatible with 'Money.SomeExchangeRate'
--
-- Example rendering an 'Money.ExchangeRate' constructed with
-- @'Money.exchangeRate' (5 '%' 7) :: 'Money.ExchangeRate' \"USD\" \"JPY\"@
--
-- @
-- [\"USD\", \"JPY\", 5, 7]
-- @
--
-- Note: The JSON serialization changed in version 0.4 (the leading
-- @"ExchangeRate"@ string was dropped from the rendered 'Ae.Array').
instance
( KnownSymbol src, KnownSymbol dst
) => Ae.ToJSON (Money.ExchangeRate src dst) where
toJSON = Ae.toJSON . Money.toSomeExchangeRate
-- | Compatible with 'Money.SomeExchangeRate'
--
-- Note: The JSON serialization changed in version 0.4. However, this instance
-- is still able to cope with the previous format.
instance
( KnownSymbol src, KnownSymbol dst
) => Ae.FromJSON (Money.ExchangeRate src dst) where
parseJSON =
maybe empty pure <=< fmap Money.fromSomeExchangeRate . Ae.parseJSON
-- | Compatible with 'Money.ExchangeRate'
--
-- Note: The JSON serialization changed in @safe-money@ version 0.4 (the leading
-- @"ExchangeRate"@ string was dropped from the rendered 'Ae.Array').
instance Ae.ToJSON Money.SomeExchangeRate where
toJSON = \ser ->
let r = Money.someExchangeRateRate ser
in Ae.toJSON (MoneyI.someExchangeRateSrcCurrency' ser,
MoneyI.someExchangeRateDstCurrency' ser,
numerator r, denominator r)
-- | Compatible with 'Money.ExchangeRate'
--
-- Note: The JSON serialization changed in @safe-money@ version 0.4. However,
-- this instance is still able to cope with the previous format.
instance Ae.FromJSON Money.SomeExchangeRate where
parseJSON = \v -> do
(src, dst, n, d) <- Ae.parseJSON v <|> do
-- Pre 0.4 format.
("ExchangeRate" :: T.Text, src, dst, n, d) <- Ae.parseJSON v
pure (src, dst, n, d)
when (d == 0) (fail "denominator is zero")
maybe empty pure (MoneyI.mkSomeExchangeRate' src dst (n % d))