{-|
Description : Invocation of the process parts with assertion checking
-}
module Language.Haskell.Formatter.Process.Formatter (Formatter(..), format)
       where
import qualified Control.Monad as Monad
import qualified Data.Function as Function
import qualified Language.Haskell.Formatter.ExactCode as ExactCode
import qualified Language.Haskell.Formatter.Process.Code as Code
import qualified Language.Haskell.Formatter.Result as Result
import qualified Language.Haskell.Formatter.Source as Source

class Coded a where

        getCode :: a -> Source.Module ()

data Formatter = Formatter{Formatter -> ExactCode -> Result CommentableCode
attachComments ::
                           ExactCode.ExactCode ->
                             Result.Result Code.CommentableCode,
                           Formatter
-> LocatableCommentableCode -> Result LocatableCommentableCode
formatActualCode ::
                           Code.LocatableCommentableCode ->
                             Result.Result Code.LocatableCommentableCode,
                           Formatter
-> LocatableCommentableCode -> Result LocatableCommentableCode
formatComments ::
                           Code.LocatableCommentableCode ->
                             Result.Result Code.LocatableCommentableCode,
                           Formatter -> LocatableCommentableCode -> Result ExactCode
detachComments ::
                           Code.LocatableCommentableCode ->
                             Result.Result ExactCode.ExactCode}

instance Coded (Source.Module a) where
        getCode :: Module a -> Module ()
getCode = Module a -> Module ()
forall (f :: * -> *) a. Functor f => f a -> f ()
Monad.void

instance Coded ExactCode.ExactCode where
        getCode :: ExactCode -> Module ()
getCode = Module SrcSpanInfo -> Module ()
forall a. Coded a => a -> Module ()
getCode (Module SrcSpanInfo -> Module ())
-> (ExactCode -> Module SrcSpanInfo) -> ExactCode -> Module ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ExactCode -> Module SrcSpanInfo
ExactCode.actualCode

format :: Formatter -> ExactCode.ExactCode -> Result.Result ExactCode.ExactCode
format :: Formatter -> ExactCode -> Result ExactCode
format Formatter
formatter ExactCode
exact
  = do CommentableCode
commentable <- Formatter -> ExactCode -> Result CommentableCode
checkedAttachComments Formatter
formatter ExactCode
exact
       LocatableCommentableCode
locatableCommentable <- Module SrcSpanInfo
-> CommentableCode -> Result LocatableCommentableCode
Code.tryZipLocationsComments Module SrcSpanInfo
locatable
                                 CommentableCode
commentable
       {- Formatting the actual code is allowed to change the code itself. -}
       LocatableCommentableCode
locatableCommentable' <- Formatter
-> LocatableCommentableCode -> Result LocatableCommentableCode
formatActualCode Formatter
formatter LocatableCommentableCode
locatableCommentable
       LocatableCommentableCode
locatableCommentable'' <- Formatter
-> LocatableCommentableCode -> Result LocatableCommentableCode
checkedFormatComments Formatter
formatter
                                   LocatableCommentableCode
locatableCommentable'
       Formatter -> LocatableCommentableCode -> Result ExactCode
checkedDetachComments Formatter
formatter LocatableCommentableCode
locatableCommentable''
  where locatable :: Module SrcSpanInfo
locatable = ExactCode -> Module SrcSpanInfo
ExactCode.actualCode ExactCode
exact

checkedAttachComments ::
                      Formatter ->
                        ExactCode.ExactCode ->
                          Result.Result Code.CommentableCode
checkedAttachComments :: Formatter -> ExactCode -> Result CommentableCode
checkedAttachComments Formatter
formatter
  = (ExactCode -> Result CommentableCode)
-> String -> ExactCode -> Result CommentableCode
forall a b.
(Coded a, Coded b) =>
(a -> Result b) -> String -> a -> Result b
transformNotes (Formatter -> ExactCode -> Result CommentableCode
attachComments Formatter
formatter) String
message
  where message :: String
message = String
"Attaching the comments changed the code itself."

transformNotes ::
                 (Coded a, Coded b) =>
                 (a -> Result.Result b) -> String -> a -> Result.Result b
transformNotes :: (a -> Result b) -> String -> a -> Result b
transformNotes a -> Result b
transform = (a -> Result b) -> (a -> b -> Bool) -> String -> a -> Result b
forall a b.
(a -> Result b) -> (a -> b -> Bool) -> String -> a -> Result b
transformWithCheck a -> Result b
transform a -> b -> Bool
forall a a. (Coded a, Coded a) => a -> a -> Bool
assert
  where assert :: a -> a -> Bool
assert a
code a
code' = a -> Module ()
forall a. Coded a => a -> Module ()
getCode a
code Module () -> Module () -> Bool
forall a. Eq a => a -> a -> Bool
== a -> Module ()
forall a. Coded a => a -> Module ()
getCode a
code'

transformWithCheck ::
                   (a -> Result.Result b) ->
                     (a -> b -> Bool) -> String -> a -> Result.Result b
transformWithCheck :: (a -> Result b) -> (a -> b -> Bool) -> String -> a -> Result b
transformWithCheck a -> Result b
transform a -> b -> Bool
assert String
errorMessage a
input
  = do b
output <- a -> Result b
transform a
input
       if a -> b -> Bool
assert a
input b
output then b -> Result b
forall (m :: * -> *) a. Monad m => a -> m a
return b
output else
         String -> Result b
forall a. String -> Result a
Result.fatalAssertionError String
errorMessage

checkedFormatComments ::
                      Formatter ->
                        Code.LocatableCommentableCode ->
                          Result.Result Code.LocatableCommentableCode
checkedFormatComments :: Formatter
-> LocatableCommentableCode -> Result LocatableCommentableCode
checkedFormatComments Formatter
formatter = (LocatableCommentableCode -> Result LocatableCommentableCode)
-> (LocatableCommentableCode -> LocatableCommentableCode -> Bool)
-> String
-> LocatableCommentableCode
-> Result LocatableCommentableCode
forall a b.
(a -> Result b) -> (a -> b -> Bool) -> String -> a -> Result b
transformWithCheck LocatableCommentableCode -> Result LocatableCommentableCode
transform LocatableCommentableCode -> LocatableCommentableCode -> Bool
assert String
message
  where transform :: LocatableCommentableCode -> Result LocatableCommentableCode
transform = Formatter
-> LocatableCommentableCode -> Result LocatableCommentableCode
formatComments Formatter
formatter
        assert :: LocatableCommentableCode -> LocatableCommentableCode -> Bool
assert = (Module SrcSpanInfo -> Module SrcSpanInfo -> Bool)
-> (LocatableCommentableCode -> Module SrcSpanInfo)
-> LocatableCommentableCode
-> LocatableCommentableCode
-> Bool
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
Function.on Module SrcSpanInfo -> Module SrcSpanInfo -> Bool
forall a. Eq a => a -> a -> Bool
(==) LocatableCommentableCode -> Module SrcSpanInfo
Code.dropComments
        message :: String
message = String
"Formatting the comments changed more than just the comments."

checkedDetachComments ::
                      Formatter ->
                        Code.LocatableCommentableCode ->
                          Result.Result ExactCode.ExactCode
checkedDetachComments :: Formatter -> LocatableCommentableCode -> Result ExactCode
checkedDetachComments Formatter
formatter
  = (LocatableCommentableCode -> Result ExactCode)
-> String -> LocatableCommentableCode -> Result ExactCode
forall a b.
(Coded a, Coded b) =>
(a -> Result b) -> String -> a -> Result b
transformNotes (Formatter -> LocatableCommentableCode -> Result ExactCode
detachComments Formatter
formatter) String
message
  where message :: String
message = String
"Detaching the comments changed the code itself."