module CabalGild.Action.EvaluatePragmas where

import qualified CabalGild.Class.MonadWalk as MonadWalk
import qualified CabalGild.Extra.ModuleName as ModuleName
import qualified CabalGild.Extra.Name as Name
import qualified CabalGild.Extra.String as String
import qualified CabalGild.Type.Comment as Comment
import qualified CabalGild.Type.Pragma as Pragma
import qualified Control.Monad as Monad
import qualified Control.Monad.Trans.Class as Trans
import qualified Control.Monad.Trans.Maybe as MaybeT
import qualified Data.Maybe as Maybe
import qualified Data.Set as Set
import qualified Distribution.Fields as Fields
import qualified Distribution.Parsec as Parsec
import qualified Distribution.Utils.Generic as Utils
import qualified System.FilePath as FilePath

-- | High level wrapper around 'fields' that makes this action easier to
-- compose with other actions.
run ::
  (MonadWalk.MonadWalk m) =>
  FilePath ->
  ([Fields.Field [Comment.Comment a]], cs) ->
  m ([Fields.Field [Comment.Comment a]], cs)
run :: forall (m :: * -> *) a cs.
MonadWalk m =>
FilePath
-> ([Field [Comment a]], cs) -> m ([Field [Comment a]], cs)
run FilePath
p ([Field [Comment a]]
fs, cs
cs) = (,) ([Field [Comment a]] -> cs -> ([Field [Comment a]], cs))
-> m [Field [Comment a]] -> m (cs -> ([Field [Comment a]], cs))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FilePath -> [Field [Comment a]] -> m [Field [Comment a]]
forall (m :: * -> *) a.
MonadWalk m =>
FilePath -> [Field [Comment a]] -> m [Field [Comment a]]
fields FilePath
p [Field [Comment a]]
fs m (cs -> ([Field [Comment a]], cs))
-> m cs -> m ([Field [Comment a]], cs)
forall a b. m (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> cs -> m cs
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure cs
cs

-- | Evaluates pragmas modules within the given fields.
fields ::
  (MonadWalk.MonadWalk m) =>
  FilePath ->
  [Fields.Field [Comment.Comment a]] ->
  m [Fields.Field [Comment.Comment a]]
fields :: forall (m :: * -> *) a.
MonadWalk m =>
FilePath -> [Field [Comment a]] -> m [Field [Comment a]]
fields = (Field [Comment a] -> m (Field [Comment a]))
-> [Field [Comment a]] -> m [Field [Comment a]]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM ((Field [Comment a] -> m (Field [Comment a]))
 -> [Field [Comment a]] -> m [Field [Comment a]])
-> (FilePath -> Field [Comment a] -> m (Field [Comment a]))
-> FilePath
-> [Field [Comment a]]
-> m [Field [Comment a]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> Field [Comment a] -> m (Field [Comment a])
forall (m :: * -> *) a.
MonadWalk m =>
FilePath -> Field [Comment a] -> m (Field [Comment a])
field

-- | Evaluates pragmas within the given field. Or, if the field is a section,
-- evaluates pragmas recursively within the fields of the section.
--
-- If modules are discovered for a field, that fields lines are completely
-- replaced. If anything goes wrong while discovering modules, the original
-- field is returned.
field ::
  (MonadWalk.MonadWalk m) =>
  FilePath ->
  Fields.Field [Comment.Comment a] ->
  m (Fields.Field [Comment.Comment a])
field :: forall (m :: * -> *) a.
MonadWalk m =>
FilePath -> Field [Comment a] -> m (Field [Comment a])
field FilePath
p Field [Comment a]
f = case Field [Comment a]
f of
  Fields.Field Name [Comment a]
n [FieldLine [Comment a]]
_ -> (Maybe (Field [Comment a]) -> Field [Comment a])
-> m (Maybe (Field [Comment a])) -> m (Field [Comment a])
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Field [Comment a] -> Maybe (Field [Comment a]) -> Field [Comment a]
forall a. a -> Maybe a -> a
Maybe.fromMaybe Field [Comment a]
f) (m (Maybe (Field [Comment a])) -> m (Field [Comment a]))
-> (MaybeT m (Field [Comment a]) -> m (Maybe (Field [Comment a])))
-> MaybeT m (Field [Comment a])
-> m (Field [Comment a])
forall b c a. (b -> c) -> (a -> b) -> a -> c
. MaybeT m (Field [Comment a]) -> m (Maybe (Field [Comment a]))
forall (m :: * -> *) a. MaybeT m a -> m (Maybe a)
MaybeT.runMaybeT (MaybeT m (Field [Comment a]) -> m (Field [Comment a]))
-> MaybeT m (Field [Comment a]) -> m (Field [Comment a])
forall a b. (a -> b) -> a -> b
$ do
    Bool -> MaybeT m ()
forall (f :: * -> *). Alternative f => Bool -> f ()
Monad.guard (Bool -> MaybeT m ()) -> Bool -> MaybeT m ()
forall a b. (a -> b) -> a -> b
$ FieldName -> Set FieldName -> Bool
forall a. Ord a => a -> Set a -> Bool
Set.member (Name [Comment a] -> FieldName
forall a. Name a -> FieldName
Name.value Name [Comment a]
n) Set FieldName
relevantFieldNames
    Comment a
c <- Maybe (Comment a) -> MaybeT m (Comment a)
forall (f :: * -> *) a. Applicative f => Maybe a -> MaybeT f a
hoistMaybe (Maybe (Comment a) -> MaybeT m (Comment a))
-> ([Comment a] -> Maybe (Comment a))
-> [Comment a]
-> MaybeT m (Comment a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Comment a] -> Maybe (Comment a)
forall a. [a] -> Maybe a
Utils.safeLast ([Comment a] -> MaybeT m (Comment a))
-> [Comment a] -> MaybeT m (Comment a)
forall a b. (a -> b) -> a -> b
$ Name [Comment a] -> [Comment a]
forall a. Name a -> a
Name.annotation Name [Comment a]
n
    Pragma
x <- Maybe Pragma -> MaybeT m Pragma
forall (f :: * -> *) a. Applicative f => Maybe a -> MaybeT f a
hoistMaybe (Maybe Pragma -> MaybeT m Pragma)
-> (FieldName -> Maybe Pragma) -> FieldName -> MaybeT m Pragma
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FieldName -> Maybe Pragma
forall a. Parsec a => FieldName -> Maybe a
Parsec.simpleParsecBS (FieldName -> MaybeT m Pragma) -> FieldName -> MaybeT m Pragma
forall a b. (a -> b) -> a -> b
$ Comment a -> FieldName
forall a. Comment a -> FieldName
Comment.value Comment a
c
    FilePath
y <- case Pragma
x of
      Pragma.Discover FilePath
y -> FilePath -> MaybeT m FilePath
forall a. a -> MaybeT m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure FilePath
y
    let d :: FilePath
d = FilePath -> FilePath -> FilePath
FilePath.combine (FilePath -> FilePath
FilePath.takeDirectory FilePath
p) FilePath
y
    [FilePath]
fs <- m [FilePath] -> MaybeT m [FilePath]
forall (m :: * -> *) a. Monad m => m a -> MaybeT m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
Trans.lift (m [FilePath] -> MaybeT m [FilePath])
-> m [FilePath] -> MaybeT m [FilePath]
forall a b. (a -> b) -> a -> b
$ FilePath -> m [FilePath]
forall (m :: * -> *). MonadWalk m => FilePath -> m [FilePath]
MonadWalk.walk FilePath
d
    Field [Comment a] -> MaybeT m (Field [Comment a])
forall a. a -> MaybeT m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure
      (Field [Comment a] -> MaybeT m (Field [Comment a]))
-> ([FilePath] -> Field [Comment a])
-> [FilePath]
-> MaybeT m (Field [Comment a])
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Name [Comment a] -> [FieldLine [Comment a]] -> Field [Comment a]
forall ann. Name ann -> [FieldLine ann] -> Field ann
Fields.Field Name [Comment a]
n
      ([FieldLine [Comment a]] -> Field [Comment a])
-> ([FilePath] -> [FieldLine [Comment a]])
-> [FilePath]
-> Field [Comment a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ModuleName -> FieldLine [Comment a])
-> [ModuleName] -> [FieldLine [Comment a]]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ([Comment a] -> ModuleName -> FieldLine [Comment a]
forall a. a -> ModuleName -> FieldLine a
ModuleName.toFieldLine [])
      ([ModuleName] -> [FieldLine [Comment a]])
-> ([FilePath] -> [ModuleName])
-> [FilePath]
-> [FieldLine [Comment a]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (FilePath -> Maybe ModuleName) -> [FilePath] -> [ModuleName]
forall a b. (a -> Maybe b) -> [a] -> [b]
Maybe.mapMaybe (FilePath -> Maybe ModuleName
ModuleName.fromFilePath (FilePath -> Maybe ModuleName)
-> (FilePath -> FilePath) -> FilePath -> Maybe ModuleName
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> FilePath -> FilePath
FilePath.makeRelative FilePath
d)
      ([FilePath] -> MaybeT m (Field [Comment a]))
-> [FilePath] -> MaybeT m (Field [Comment a])
forall a b. (a -> b) -> a -> b
$ (FilePath -> Maybe FilePath) -> [FilePath] -> [FilePath]
forall a b. (a -> Maybe b) -> [a] -> [b]
Maybe.mapMaybe (FilePath -> FilePath -> Maybe FilePath
FilePath.stripExtension FilePath
"hs") [FilePath]
fs
  Fields.Section Name [Comment a]
n [SectionArg [Comment a]]
sas [Field [Comment a]]
fs -> Name [Comment a]
-> [SectionArg [Comment a]]
-> [Field [Comment a]]
-> Field [Comment a]
forall ann.
Name ann -> [SectionArg ann] -> [Field ann] -> Field ann
Fields.Section Name [Comment a]
n [SectionArg [Comment a]]
sas ([Field [Comment a]] -> Field [Comment a])
-> m [Field [Comment a]] -> m (Field [Comment a])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FilePath -> [Field [Comment a]] -> m [Field [Comment a]]
forall (m :: * -> *) a.
MonadWalk m =>
FilePath -> [Field [Comment a]] -> m [Field [Comment a]]
fields FilePath
p [Field [Comment a]]
fs

-- | These are the names of the fields that can have this action applied to
-- them.
relevantFieldNames :: Set.Set Fields.FieldName
relevantFieldNames :: Set FieldName
relevantFieldNames =
  [FieldName] -> Set FieldName
forall a. Ord a => [a] -> Set a
Set.fromList ([FieldName] -> Set FieldName) -> [FieldName] -> Set FieldName
forall a b. (a -> b) -> a -> b
$
    (FilePath -> FieldName) -> [FilePath] -> [FieldName]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap
      FilePath -> FieldName
String.toUtf8
      [ FilePath
"exposed-modules",
        FilePath
"other-modules"
      ]

-- | This was added in @transformers-0.6.0.0@. See
-- <https://hub.darcs.net/ross/transformers/issue/49>.
hoistMaybe :: (Applicative f) => Maybe a -> MaybeT.MaybeT f a
hoistMaybe :: forall (f :: * -> *) a. Applicative f => Maybe a -> MaybeT f a
hoistMaybe = f (Maybe a) -> MaybeT f a
forall (m :: * -> *) a. m (Maybe a) -> MaybeT m a
MaybeT.MaybeT (f (Maybe a) -> MaybeT f a)
-> (Maybe a -> f (Maybe a)) -> Maybe a -> MaybeT f a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe a -> f (Maybe a)
forall a. a -> f a
forall (f :: * -> *) a. Applicative f => a -> f a
pure