{-# LANGUAGE DefaultSignatures          #-}
{-# LANGUAGE DeriveFunctor              #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE TemplateHaskell            #-}

module Data.API.Changes.Types
    ( -- * Changelog representation
      APIWithChangelog
    , APIChangelog(..)
    , APIChange(..)
    , MigrationTag
    , VersionExtra(..)
    , showVersionExtra

    , UpdateTypePos(..)
    , UpdateDeclPos(..)
    , APITableChange(..)
    ) where

import           Data.API.PP
import           Data.API.NormalForm
import           Data.API.Types

import           Data.Map ( Map )
import           Data.Version


------------------
-- The key types
--

type APIWithChangelog = (API, APIChangelog)

-- | An API changelog, consisting of a list of versions with the
-- changes from one version to the next.  The versions must be in
-- descending order (according to the 'Ord' 'Version' instance).
data APIChangelog =
     -- | The changes from the previous version up to this version.
     ChangesUpTo VersionExtra [APIChange] APIChangelog
     -- | The initial version
   | ChangesStart Version
    deriving (APIChangelog -> APIChangelog -> Bool
(APIChangelog -> APIChangelog -> Bool)
-> (APIChangelog -> APIChangelog -> Bool) -> Eq APIChangelog
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: APIChangelog -> APIChangelog -> Bool
$c/= :: APIChangelog -> APIChangelog -> Bool
== :: APIChangelog -> APIChangelog -> Bool
$c== :: APIChangelog -> APIChangelog -> Bool
Eq, Int -> APIChangelog -> ShowS
[APIChangelog] -> ShowS
APIChangelog -> String
(Int -> APIChangelog -> ShowS)
-> (APIChangelog -> String)
-> ([APIChangelog] -> ShowS)
-> Show APIChangelog
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [APIChangelog] -> ShowS
$cshowList :: [APIChangelog] -> ShowS
show :: APIChangelog -> String
$cshow :: APIChangelog -> String
showsPrec :: Int -> APIChangelog -> ShowS
$cshowsPrec :: Int -> APIChangelog -> ShowS
Show)

-- | A single change within a changelog
data APIChange
    = ChAddType       TypeName NormTypeDecl
    | ChDeleteType    TypeName
    | ChRenameType    TypeName TypeName

      -- Specific changes for record types
    | ChAddField      TypeName FieldName APIType (Maybe DefaultValue)
    | ChDeleteField   TypeName FieldName
    | ChRenameField   TypeName FieldName FieldName
    | ChChangeField   TypeName FieldName APIType MigrationTag

      -- Changes for union types
    | ChAddUnionAlt    TypeName FieldName APIType
    | ChDeleteUnionAlt TypeName FieldName
    | ChRenameUnionAlt TypeName FieldName FieldName

      -- Changes for enum types
    | ChAddEnumVal    TypeName FieldName
    | ChDeleteEnumVal TypeName FieldName
    | ChRenameEnumVal TypeName FieldName FieldName

      -- Custom migrations
    | ChCustomType    TypeName MigrationTag
    | ChCustomAll     MigrationTag
    deriving (APIChange -> APIChange -> Bool
(APIChange -> APIChange -> Bool)
-> (APIChange -> APIChange -> Bool) -> Eq APIChange
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: APIChange -> APIChange -> Bool
$c/= :: APIChange -> APIChange -> Bool
== :: APIChange -> APIChange -> Bool
$c== :: APIChange -> APIChange -> Bool
Eq, Int -> APIChange -> ShowS
[APIChange] -> ShowS
APIChange -> String
(Int -> APIChange -> ShowS)
-> (APIChange -> String)
-> ([APIChange] -> ShowS)
-> Show APIChange
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [APIChange] -> ShowS
$cshowList :: [APIChange] -> ShowS
show :: APIChange -> String
$cshow :: APIChange -> String
showsPrec :: Int -> APIChange -> ShowS
$cshowsPrec :: Int -> APIChange -> ShowS
Show)

instance PPLines APIChange where
  ppLines :: APIChange -> [String]
ppLines (ChAddType TypeName
t NormTypeDecl
d)           = (String
"added " String -> ShowS
forall a. [a] -> [a] -> [a]
++ TypeName -> String
forall t. PP t => t -> String
pp TypeName
t String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" ") String -> [String] -> [String]
`inFrontOf` NormTypeDecl -> [String]
forall t. PPLines t => t -> [String]
ppLines NormTypeDecl
d
  ppLines (ChDeleteType TypeName
t)          = [String
"removed " String -> ShowS
forall a. [a] -> [a] -> [a]
++ TypeName -> String
forall t. PP t => t -> String
pp TypeName
t]
  ppLines (ChRenameType TypeName
t TypeName
t')       = [String
"renamed " String -> ShowS
forall a. [a] -> [a] -> [a]
++ TypeName -> String
forall t. PP t => t -> String
pp TypeName
t String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" to " String -> ShowS
forall a. [a] -> [a] -> [a]
++ TypeName -> String
forall t. PP t => t -> String
pp TypeName
t']
  ppLines (ChAddField TypeName
t FieldName
f APIType
ty Maybe DefaultValue
mb_v)  = [ String
"changed record " String -> ShowS
forall a. [a] -> [a] -> [a]
++ TypeName -> String
forall t. PP t => t -> String
pp TypeName
t
                                      , String
"  field added " String -> ShowS
forall a. [a] -> [a] -> [a]
++ FieldName -> String
forall t. PP t => t -> String
pp FieldName
f String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" :: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ APIType -> String
forall t. PP t => t -> String
pp APIType
ty
                                        String -> ShowS
forall a. [a] -> [a] -> [a]
++ String -> (DefaultValue -> String) -> Maybe DefaultValue -> String
forall b a. b -> (a -> b) -> Maybe a -> b
maybe String
"" (\ DefaultValue
v -> String
" default " String -> ShowS
forall a. [a] -> [a] -> [a]
++ DefaultValue -> String
forall t. PP t => t -> String
pp DefaultValue
v) Maybe DefaultValue
mb_v]
  ppLines (ChDeleteField TypeName
t FieldName
f)       = [String
"changed record " String -> ShowS
forall a. [a] -> [a] -> [a]
++ TypeName -> String
forall t. PP t => t -> String
pp TypeName
t, String
"  field removed " String -> ShowS
forall a. [a] -> [a] -> [a]
++ FieldName -> String
forall t. PP t => t -> String
pp FieldName
f]
  ppLines (ChRenameField TypeName
t FieldName
f FieldName
f')    = [ String
"changed record " String -> ShowS
forall a. [a] -> [a] -> [a]
++ TypeName -> String
forall t. PP t => t -> String
pp TypeName
t
                                      , String
"  field renamed " String -> ShowS
forall a. [a] -> [a] -> [a]
++ FieldName -> String
forall t. PP t => t -> String
pp FieldName
f String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" to " String -> ShowS
forall a. [a] -> [a] -> [a]
++ FieldName -> String
forall t. PP t => t -> String
pp FieldName
f']
  ppLines (ChChangeField TypeName
t FieldName
f APIType
ty String
c)  = [ String
"changed record " String -> ShowS
forall a. [a] -> [a] -> [a]
++ TypeName -> String
forall t. PP t => t -> String
pp TypeName
t
                                      , String
"  field changed " String -> ShowS
forall a. [a] -> [a] -> [a]
++ FieldName -> String
forall t. PP t => t -> String
pp FieldName
f String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" :: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ APIType -> String
forall t. PP t => t -> String
pp APIType
ty
                                        String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" migration " String -> ShowS
forall a. [a] -> [a] -> [a]
++ ShowS
forall t. PP t => t -> String
pp String
c]
  ppLines (ChAddUnionAlt TypeName
t FieldName
f APIType
ty)    = [ String
"changed union " String -> ShowS
forall a. [a] -> [a] -> [a]
++ TypeName -> String
forall t. PP t => t -> String
pp TypeName
t
                                      , String
"  alternative added " String -> ShowS
forall a. [a] -> [a] -> [a]
++ FieldName -> String
forall t. PP t => t -> String
pp FieldName
f String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" :: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ APIType -> String
forall t. PP t => t -> String
pp APIType
ty]
  ppLines (ChDeleteUnionAlt TypeName
t FieldName
f)    = [ String
"changed union " String -> ShowS
forall a. [a] -> [a] -> [a]
++ TypeName -> String
forall t. PP t => t -> String
pp TypeName
t
                                      , String
"  alternative removed " String -> ShowS
forall a. [a] -> [a] -> [a]
++ FieldName -> String
forall t. PP t => t -> String
pp FieldName
f]
  ppLines (ChRenameUnionAlt TypeName
t FieldName
f FieldName
f') = [ String
"changed union " String -> ShowS
forall a. [a] -> [a] -> [a]
++ TypeName -> String
forall t. PP t => t -> String
pp TypeName
t
                                      , String
"  alternative renamed " String -> ShowS
forall a. [a] -> [a] -> [a]
++ FieldName -> String
forall t. PP t => t -> String
pp FieldName
f String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" to " String -> ShowS
forall a. [a] -> [a] -> [a]
++ FieldName -> String
forall t. PP t => t -> String
pp FieldName
f']
  ppLines (ChAddEnumVal TypeName
t FieldName
f)        = [ String
"changed enum " String -> ShowS
forall a. [a] -> [a] -> [a]
++ TypeName -> String
forall t. PP t => t -> String
pp TypeName
t
                                      , String
"  alternative added " String -> ShowS
forall a. [a] -> [a] -> [a]
++ FieldName -> String
forall t. PP t => t -> String
pp FieldName
f]
  ppLines (ChDeleteEnumVal TypeName
t FieldName
f)     = [ String
"changed enum " String -> ShowS
forall a. [a] -> [a] -> [a]
++ TypeName -> String
forall t. PP t => t -> String
pp TypeName
t
                                      , String
"  alternative removed " String -> ShowS
forall a. [a] -> [a] -> [a]
++ FieldName -> String
forall t. PP t => t -> String
pp FieldName
f]
  ppLines (ChRenameEnumVal TypeName
t FieldName
f FieldName
f')  = [ String
"changed enum " String -> ShowS
forall a. [a] -> [a] -> [a]
++ TypeName -> String
forall t. PP t => t -> String
pp TypeName
t
                                      , String
"  alternative renamed " String -> ShowS
forall a. [a] -> [a] -> [a]
++ FieldName -> String
forall t. PP t => t -> String
pp FieldName
f String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" to " String -> ShowS
forall a. [a] -> [a] -> [a]
++ FieldName -> String
forall t. PP t => t -> String
pp FieldName
f']
  ppLines (ChCustomType TypeName
t String
c)        = [String
"migration record " String -> ShowS
forall a. [a] -> [a] -> [a]
++ TypeName -> String
forall t. PP t => t -> String
pp TypeName
t String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" " String -> ShowS
forall a. [a] -> [a] -> [a]
++ ShowS
forall t. PP t => t -> String
pp String
c]
  ppLines (ChCustomAll String
c)           = [String
"migration " String -> ShowS
forall a. [a] -> [a] -> [a]
++ ShowS
forall t. PP t => t -> String
pp String
c]

-- | Within the changelog, custom migrations are represented as
-- strings, so we have less type-safety.
type MigrationTag = String

-- | Represents either a released version (with a version number) or
-- the version under development, which is newer than any release
data VersionExtra = Release Version
                  | DevVersion
  deriving (VersionExtra -> VersionExtra -> Bool
(VersionExtra -> VersionExtra -> Bool)
-> (VersionExtra -> VersionExtra -> Bool) -> Eq VersionExtra
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: VersionExtra -> VersionExtra -> Bool
$c/= :: VersionExtra -> VersionExtra -> Bool
== :: VersionExtra -> VersionExtra -> Bool
$c== :: VersionExtra -> VersionExtra -> Bool
Eq, Eq VersionExtra
Eq VersionExtra
-> (VersionExtra -> VersionExtra -> Ordering)
-> (VersionExtra -> VersionExtra -> Bool)
-> (VersionExtra -> VersionExtra -> Bool)
-> (VersionExtra -> VersionExtra -> Bool)
-> (VersionExtra -> VersionExtra -> Bool)
-> (VersionExtra -> VersionExtra -> VersionExtra)
-> (VersionExtra -> VersionExtra -> VersionExtra)
-> Ord VersionExtra
VersionExtra -> VersionExtra -> Bool
VersionExtra -> VersionExtra -> Ordering
VersionExtra -> VersionExtra -> VersionExtra
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: VersionExtra -> VersionExtra -> VersionExtra
$cmin :: VersionExtra -> VersionExtra -> VersionExtra
max :: VersionExtra -> VersionExtra -> VersionExtra
$cmax :: VersionExtra -> VersionExtra -> VersionExtra
>= :: VersionExtra -> VersionExtra -> Bool
$c>= :: VersionExtra -> VersionExtra -> Bool
> :: VersionExtra -> VersionExtra -> Bool
$c> :: VersionExtra -> VersionExtra -> Bool
<= :: VersionExtra -> VersionExtra -> Bool
$c<= :: VersionExtra -> VersionExtra -> Bool
< :: VersionExtra -> VersionExtra -> Bool
$c< :: VersionExtra -> VersionExtra -> Bool
compare :: VersionExtra -> VersionExtra -> Ordering
$ccompare :: VersionExtra -> VersionExtra -> Ordering
$cp1Ord :: Eq VersionExtra
Ord, Int -> VersionExtra -> ShowS
[VersionExtra] -> ShowS
VersionExtra -> String
(Int -> VersionExtra -> ShowS)
-> (VersionExtra -> String)
-> ([VersionExtra] -> ShowS)
-> Show VersionExtra
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [VersionExtra] -> ShowS
$cshowList :: [VersionExtra] -> ShowS
show :: VersionExtra -> String
$cshow :: VersionExtra -> String
showsPrec :: Int -> VersionExtra -> ShowS
$cshowsPrec :: Int -> VersionExtra -> ShowS
Show)

showVersionExtra :: VersionExtra -> String
showVersionExtra :: VersionExtra -> String
showVersionExtra (Release Version
v) = Version -> String
showVersion Version
v
showVersionExtra VersionExtra
DevVersion  = String
"development"

instance PP VersionExtra where
  pp :: VersionExtra -> String
pp = VersionExtra -> String
showVersionExtra


------------------------
-- Representing updates
--

-- | Represents the positions in a declaration to apply an update
data UpdateDeclPos
    = UpdateHere   (Maybe UpdateDeclPos)
    | UpdateRecord (Map FieldName (Maybe UpdateTypePos))
    | UpdateUnion  (Map FieldName (Maybe UpdateTypePos))
    | UpdateType   UpdateTypePos
    deriving (UpdateDeclPos -> UpdateDeclPos -> Bool
(UpdateDeclPos -> UpdateDeclPos -> Bool)
-> (UpdateDeclPos -> UpdateDeclPos -> Bool) -> Eq UpdateDeclPos
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: UpdateDeclPos -> UpdateDeclPos -> Bool
$c/= :: UpdateDeclPos -> UpdateDeclPos -> Bool
== :: UpdateDeclPos -> UpdateDeclPos -> Bool
$c== :: UpdateDeclPos -> UpdateDeclPos -> Bool
Eq, Int -> UpdateDeclPos -> ShowS
[UpdateDeclPos] -> ShowS
UpdateDeclPos -> String
(Int -> UpdateDeclPos -> ShowS)
-> (UpdateDeclPos -> String)
-> ([UpdateDeclPos] -> ShowS)
-> Show UpdateDeclPos
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [UpdateDeclPos] -> ShowS
$cshowList :: [UpdateDeclPos] -> ShowS
show :: UpdateDeclPos -> String
$cshow :: UpdateDeclPos -> String
showsPrec :: Int -> UpdateDeclPos -> ShowS
$cshowsPrec :: Int -> UpdateDeclPos -> ShowS
Show)

-- | Represents the positions in a type to apply an update
data UpdateTypePos
    = UpdateList UpdateTypePos
    | UpdateMaybe UpdateTypePos
    | UpdateNamed TypeName
    deriving (UpdateTypePos -> UpdateTypePos -> Bool
(UpdateTypePos -> UpdateTypePos -> Bool)
-> (UpdateTypePos -> UpdateTypePos -> Bool) -> Eq UpdateTypePos
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: UpdateTypePos -> UpdateTypePos -> Bool
$c/= :: UpdateTypePos -> UpdateTypePos -> Bool
== :: UpdateTypePos -> UpdateTypePos -> Bool
$c== :: UpdateTypePos -> UpdateTypePos -> Bool
Eq, Int -> UpdateTypePos -> ShowS
[UpdateTypePos] -> ShowS
UpdateTypePos -> String
(Int -> UpdateTypePos -> ShowS)
-> (UpdateTypePos -> String)
-> ([UpdateTypePos] -> ShowS)
-> Show UpdateTypePos
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [UpdateTypePos] -> ShowS
$cshowList :: [UpdateTypePos] -> ShowS
show :: UpdateTypePos -> String
$cshow :: UpdateTypePos -> String
showsPrec :: Int -> UpdateTypePos -> ShowS
$cshowsPrec :: Int -> UpdateTypePos -> ShowS
Show)

data APITableChange
      -- | An initial API, an APIChange and the positions in which to apply it
    = APIChange NormAPI APIChange (Map TypeName UpdateDeclPos)
      -- | Request to validate the dataset against the given API
    | ValidateData NormAPI
    deriving (APITableChange -> APITableChange -> Bool
(APITableChange -> APITableChange -> Bool)
-> (APITableChange -> APITableChange -> Bool) -> Eq APITableChange
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: APITableChange -> APITableChange -> Bool
$c/= :: APITableChange -> APITableChange -> Bool
== :: APITableChange -> APITableChange -> Bool
$c== :: APITableChange -> APITableChange -> Bool
Eq, Int -> APITableChange -> ShowS
[APITableChange] -> ShowS
APITableChange -> String
(Int -> APITableChange -> ShowS)
-> (APITableChange -> String)
-> ([APITableChange] -> ShowS)
-> Show APITableChange
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [APITableChange] -> ShowS
$cshowList :: [APITableChange] -> ShowS
show :: APITableChange -> String
$cshow :: APITableChange -> String
showsPrec :: Int -> APITableChange -> ShowS
$cshowsPrec :: Int -> APITableChange -> ShowS
Show)

instance PPLines APITableChange where
  ppLines :: APITableChange -> [String]
ppLines (APIChange NormAPI
_ APIChange
c Map TypeName UpdateDeclPos
_)  = APIChange -> [String]
forall t. PPLines t => t -> [String]
ppLines APIChange
c
  ppLines (ValidateData NormAPI
_) = []