{-# LANGUAGE LambdaCase        #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes       #-}
{-# LANGUAGE RecordWildCards   #-}
{-# LANGUAGE TupleSections     #-}
{-# LANGUAGE TypeApplications  #-}

{-|
Module      : Headroom.FileSupport.PureScript
Description : Support for /PureScript/ source code files
Copyright   : (c) 2019-2021 Vaclav Svejcar
License     : BSD-3-Clause
Maintainer  : vaclav.svejcar@gmail.com
Stability   : experimental
Portability : POSIX

Support for /PureScript/ source code files. This implementation extracts
/PureScript/ module name as variable.

= Extracted Variables for Templates
This implementation extracts following variables from source code file:

* @___purescript_module_name__@ - name of the /PureScript/ module

= Extracted Custom Data
This implementation does not extract any custom data from template file.
-}

module Headroom.FileSupport.PureScript
  ( fileSupport
  )
where

import           Headroom.Data.Regex                 ( isMatch
                                                     , match
                                                     , re
                                                     )
import           Headroom.FileSupport.TemplateData   ( TemplateData(..) )
import           Headroom.FileSupport.Types          ( FileSupport(..)
                                                     , SyntaxAnalysis(..)
                                                     )
import           Headroom.FileType.Types             ( FileType(PureScript) )
import           Headroom.Header.Types               ( HeaderTemplate )
import           Headroom.SourceCode                 ( LineType(..)
                                                     , SourceCode(..)
                                                     , firstMatching
                                                     )
import           Headroom.Variables                  ( mkVariables )
import           Headroom.Variables.Types            ( Variables(..) )
import           RIO
import           RIO.Lens                            ( ix )


------------------------------  PUBLIC FUNCTIONS  ------------------------------

-- | Implementation of 'FileSupport' for /PureScript/.
fileSupport :: FileSupport
fileSupport :: FileSupport
fileSupport = FileSupport :: SyntaxAnalysis
-> ExtractTemplateDataFn
-> ExtractVariablesFn
-> FileType
-> FileSupport
FileSupport
  { fsSyntaxAnalysis :: SyntaxAnalysis
fsSyntaxAnalysis      = SyntaxAnalysis
syntaxAnalysis
  , fsExtractTemplateData :: ExtractTemplateDataFn
fsExtractTemplateData = (HeaderSyntax -> TemplateData) -> a -> HeaderSyntax -> TemplateData
forall a b. a -> b -> a
const ((HeaderSyntax -> TemplateData)
 -> a -> HeaderSyntax -> TemplateData)
-> (TemplateData -> HeaderSyntax -> TemplateData)
-> TemplateData
-> a
-> HeaderSyntax
-> TemplateData
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TemplateData -> HeaderSyntax -> TemplateData
forall a b. a -> b -> a
const (TemplateData -> a -> HeaderSyntax -> TemplateData)
-> TemplateData -> a -> HeaderSyntax -> TemplateData
forall a b. (a -> b) -> a -> b
$ TemplateData
NoTemplateData
  , fsExtractVariables :: ExtractVariablesFn
fsExtractVariables    = ExtractVariablesFn
extractVariables
  , fsFileType :: FileType
fsFileType            = FileType
PureScript
  }


------------------------------  PRIVATE FUNCTIONS  -----------------------------

syntaxAnalysis :: SyntaxAnalysis
syntaxAnalysis :: SyntaxAnalysis
syntaxAnalysis = SyntaxAnalysis :: (Text -> Bool) -> (Text -> Bool) -> SyntaxAnalysis
SyntaxAnalysis
  { saIsCommentStart :: Text -> Bool
saIsCommentStart = Regex -> Text -> Bool
isMatch [re|^{-(?!\h*#)|^--|]
  , saIsCommentEnd :: Text -> Bool
saIsCommentEnd   = Regex -> Text -> Bool
isMatch [re|^\h*-}|\w+\h*-}|^--|]
  }


extractVariables :: HeaderTemplate
                 -> Maybe (Int, Int)
                 -> SourceCode
                 -> Variables
extractVariables :: ExtractVariablesFn
extractVariables HeaderTemplate
_ Maybe (Int, Int)
_ SourceCode
source = ([(Text, Text)] -> Variables
mkVariables ([(Text, Text)] -> Variables)
-> ([Maybe (Text, Text)] -> [(Text, Text)])
-> [Maybe (Text, Text)]
-> Variables
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Maybe (Text, Text)] -> [(Text, Text)]
forall a. [Maybe a] -> [a]
catMaybes)
  [(Text
"_purescript_module_name", ) (Text -> (Text, Text)) -> Maybe Text -> Maybe (Text, Text)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SourceCode -> Maybe Text
extractModuleName SourceCode
source]


extractModuleName :: SourceCode -> Maybe Text
extractModuleName :: SourceCode -> Maybe Text
extractModuleName = ((Int, Text) -> Text) -> Maybe (Int, Text) -> Maybe Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Int, Text) -> Text
forall a b. (a, b) -> b
snd (Maybe (Int, Text) -> Maybe Text)
-> (SourceCode -> Maybe (Int, Text)) -> SourceCode -> Maybe Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (CodeLine -> Maybe Text) -> SourceCode -> Maybe (Int, Text)
forall a. (CodeLine -> Maybe a) -> SourceCode -> Maybe (Int, a)
firstMatching CodeLine -> Maybe Text
f
 where
  f :: CodeLine -> Maybe Text
f (LineType
lt, Text
l) | LineType
lt LineType -> LineType -> Bool
forall a. Eq a => a -> a -> Bool
== LineType
Code = Regex -> Text -> Maybe [Text]
match [re|^module\s+(\S+)|] Text
l Maybe [Text] -> ([Text] -> Maybe Text) -> Maybe Text
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= ([Text] -> Getting (First Text) [Text] Text -> Maybe Text
forall s a. s -> Getting (First a) s a -> Maybe a
^? Index [Text] -> Traversal' [Text] (IxValue [Text])
forall m. Ixed m => Index m -> Traversal' m (IxValue m)
ix Index [Text]
1)
            | Bool
otherwise  = Maybe Text
forall a. Maybe a
Nothing