packages feed

schematic-0.1.2.0: src/Data/Schematic.hs

{-# OPTIONS -fprint-explicit-kinds #-}
{-# LANGUAGE AllowAmbiguousTypes #-}

module Data.Schematic
  ( module Data.Schematic.Schema
  , module Data.Schematic.Migration
  , module Data.Schematic.Utils
  , decodeAndValidateJson
  , parseAndValidateJson
  , parseAndValidateVersionedJson
  , parseAndValidateTopVersionJson
  , decodeAndValidateVersionedJson
  , isValid
  , isDecodingError
  , isValidationError
  , ParseResult(..)
  , Migratable
  ) where

import Control.Monad.Validation
import Data.Aeson as J
import Data.Aeson.Types as J
import Data.ByteString.Lazy as BL
import Data.Functor.Identity
import Data.Schematic.Migration
import Data.Schematic.Schema
import Data.Schematic.Utils
import Data.Schematic.Validation
import Data.Singletons.Prelude
import Data.Text as T


parseAndValidateJson
  :: forall schema
  .  (J.FromJSON (JsonRepr schema), TopLevel schema, SingI schema)
  => J.Value
  -> ParseResult (JsonRepr schema)
parseAndValidateJson v =
  case parseEither parseJSON v of
    Left s         -> DecodingError $ T.pack s
    Right jsonRepr ->
      let
        validate = validateJsonRepr (sing :: Sing schema) [] jsonRepr
        res      = runIdentity . runValidationTEither $ validate
      in case res of
        Left em  -> ValidationError em
        Right () -> Valid jsonRepr

parseAndValidateTopVersionJson
  :: forall proxy (v :: Versioned)
  .  (SingI (TopVersion (AllVersions v)))
  => proxy v
  -> J.Value
  -> ParseResult (JsonRepr (TopVersion (AllVersions v)))
parseAndValidateTopVersionJson _ v =
  case parseEither parseJSON v of
    Left s -> DecodingError $ T.pack s
    Right jsonRepr ->
      let
        validate =
          validateJsonRepr (sing :: Sing (TopVersion (AllVersions v))) [] jsonRepr
        res      = runIdentity . runValidationTEither $ validate
      in case res of
        Left em  -> ValidationError em
        Right () -> Valid jsonRepr

class Migratable (revisions :: [(Revision, Schema)]) where
  mparse
    :: Sing revisions
    -> J.Value
    -> ParseResult (JsonRepr (Snd (Head revisions)))

instance {-# OVERLAPPING #-} (TopLevel (Snd rev), SingI (Snd rev)) => Migratable '[rev] where
  mparse _ = parseAndValidateJson

instance {-# OVERLAPPABLE #-}
  ( Migratable (Tail revisions)
  , MigrateSchema (Snd (Head (Tail revisions))) (Snd (Head revisions))
  , SingI (Snd (Head revisions)))
  => Migratable revisions where
  mparse s v = case parseEither parseJSON v of
    Left _  -> migrate <$> mparse (sTail s) v
    Right x -> Valid x
-- type family Migratable (revisions :: [(Revision, Schema)]) = (cs :: Constraint) where
--   Migratable '[rev] = (TopLevel (Snd rev), SingI (Snd rev))
--   Migratable  revisions =
--    ( Migratable (Tail revisions)
--    , MigrateSchema (Snd (Head (Tail revisions))) (Snd (Head revisions))
--    , SingI (Snd (Head revisions)))

-- mparseBySing
--   :: Sing schema
--   -> J.Value
--   -> ParseResult (JsonRepr (Snd (Head revisions)))
-- mparseBySing s v = undefined

-- mparse
--   :: Migratable revisions
--   => Sing revisions
--   -> J.Value
--   -> ParseResult (JsonRepr (Snd (Head revisions)))
-- mparse s v =
--   let
--     st = sTail s
--     sth = sSnd $ sHead st
--   SCons x SNil -> parseAndValidateJson v
--   SCons sch _   -> case parseEither parseJSON v of
--     Left _  -> migrate <$> mparse (sTail s) v
--     Right x -> Valid x

parseAndValidateVersionedJson
  :: forall proxy v. (SingI (AllVersions v), Migratable (AllVersions v))
  => proxy v
  -> J.Value
  -> ParseResult (JsonRepr (Snd (Head (AllVersions v))))
parseAndValidateVersionedJson _ v = mparse (sing :: Sing (AllVersions v)) v

decodeAndValidateJson
  :: forall schema
  .  (J.FromJSON (JsonRepr schema), TopLevel schema, SingI schema)
  => BL.ByteString
  -> ParseResult (JsonRepr schema)
decodeAndValidateJson bs = case decode bs of
  Nothing -> DecodingError "malformed json"
  Just x  -> parseAndValidateJson x

decodeAndValidateVersionedJson
  :: (Migratable (AllVersions versioned), SingI (AllVersions versioned))
  => proxy versioned
  -> BL.ByteString
  -> ParseResult (JsonRepr (Snd (Head (AllVersions versioned))))
decodeAndValidateVersionedJson vp bs = case decode bs of
  Nothing -> DecodingError "malformed json"
  Just x  -> parseAndValidateVersionedJson vp x