content: {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE GeneralizedNewtypeDeriving #-}
  {-# LANGUAGE ViewPatterns #-}
  module Hpack.Syntax.BuildTools (
    BuildTools(..)
  , ParseBuildTool(..)
  , SystemBuildTools(..)
  ) where
  
  import           Imports
  
  import qualified Control.Monad.Fail as Fail
  import qualified Data.Text as T
  import qualified Distribution.Package as D
  import           Data.Map.Lazy (Map)
  import qualified Data.Map.Lazy as Map
  
  import qualified Distribution.Types.ExeDependency as D
  import qualified Distribution.Types.UnqualComponentName as D
  import qualified Distribution.Types.LegacyExeDependency as D
  
  import           Data.Aeson.Config.FromValue
  
  import           Hpack.Syntax.DependencyVersion
  import           Hpack.Syntax.Dependencies (parseDependency)
  
  import           Hpack.Syntax.ParseDependencies
  
  data ParseBuildTool = QualifiedBuildTool String String | UnqualifiedBuildTool String
    deriving (Show, Eq)
  
  newtype BuildTools = BuildTools {
    unBuildTools :: [(ParseBuildTool, DependencyVersion)]
  } deriving (Show, Eq, Semigroup, Monoid)
  
  instance FromValue BuildTools where
    fromValue = fmap BuildTools . parseDependencies parse
      where
        parse :: Parse ParseBuildTool DependencyVersion
        parse = Parse {
          parseString = buildToolFromString
        , parseListItem = objectDependency
        , parseDictItem = dependencyVersion
        , parseName = nameToBuildTool
        }
  
        nameToBuildTool :: Text -> ParseBuildTool
        nameToBuildTool (T.unpack -> name) = case break (== ':') name of
          (executable, "") -> UnqualifiedBuildTool executable
          (package, executable) -> QualifiedBuildTool package (drop 1 executable)
  
        buildToolFromString :: Text -> Parser (ParseBuildTool, DependencyVersion)
        buildToolFromString s = parseQualifiedBuildTool s <|> parseUnqualifiedBuildTool s
  
        parseQualifiedBuildTool :: Fail.MonadFail m => Text -> m (ParseBuildTool, DependencyVersion)
        parseQualifiedBuildTool = fmap fromCabal . cabalParse "build tool" . T.unpack
          where
            fromCabal :: D.ExeDependency -> (ParseBuildTool, DependencyVersion)
            fromCabal (D.ExeDependency package executable version) = (
                QualifiedBuildTool (D.unPackageName package) (D.unUnqualComponentName executable)
              , DependencyVersion Nothing $ versionConstraintFromCabal version
              )
  
        parseUnqualifiedBuildTool :: Fail.MonadFail m => Text -> m (ParseBuildTool, DependencyVersion)
        parseUnqualifiedBuildTool = fmap (first UnqualifiedBuildTool) . parseDependency "build tool"
  
  newtype SystemBuildTools = SystemBuildTools {
    unSystemBuildTools :: Map String VersionConstraint
  } deriving (Show, Eq, Semigroup, Monoid)
  
  instance FromValue SystemBuildTools where
    fromValue = fmap (SystemBuildTools . Map.fromList) . parseDependencies parse
      where
        parse :: Parse String VersionConstraint
        parse = Parse {
          parseString = parseSystemBuildTool
        , parseListItem = (.: "version")
        , parseDictItem = versionConstraint
        , parseName = T.unpack
        }
  
        parseSystemBuildTool :: Fail.MonadFail m => Text -> m (String, VersionConstraint)
        parseSystemBuildTool = fmap fromCabal . cabalParse "system build tool" . T.unpack
          where
            fromCabal :: D.LegacyExeDependency -> (String, VersionConstraint)
            fromCabal (D.LegacyExeDependency name version) = (name, versionConstraintFromCabal version)
  