{-# LANGUAGE OverloadedStrings #-}
module Ide.Plugin.Cabal.Parse
( parseCabalFileContents
, readCabalFields
) where

import qualified Data.ByteString                              as BS
import           Data.List.NonEmpty                           (NonEmpty (..))
import           Distribution.Fields                          (PError (..),
                                                               PWarning (..))
import           Distribution.Fields.ParseResult              (runParseResult)
import           Distribution.PackageDescription.Parsec       (parseGenericPackageDescription)
import           Distribution.Types.GenericPackageDescription (GenericPackageDescription (..))
import           Distribution.Types.Version                   (Version)
import qualified Ide.Plugin.Cabal.Diagnostics                 as Diagnostics

import qualified Data.Text                                    as T
import           Development.IDE
import qualified Distribution.Fields.Parser                   as Syntax
import qualified Distribution.Parsec.Position                 as Syntax


parseCabalFileContents
  :: BS.ByteString -- ^ UTF-8 encoded bytestring
  -> IO ([PWarning], Either (Maybe Version, NonEmpty PError) GenericPackageDescription)
parseCabalFileContents :: ByteString
-> IO
     ([PWarning],
      Either (Maybe Version, NonEmpty PError) GenericPackageDescription)
parseCabalFileContents ByteString
bs =
  ([PWarning],
 Either (Maybe Version, NonEmpty PError) GenericPackageDescription)
-> IO
     ([PWarning],
      Either (Maybe Version, NonEmpty PError) GenericPackageDescription)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (([PWarning],
  Either (Maybe Version, NonEmpty PError) GenericPackageDescription)
 -> IO
      ([PWarning],
       Either (Maybe Version, NonEmpty PError) GenericPackageDescription))
-> ([PWarning],
    Either (Maybe Version, NonEmpty PError) GenericPackageDescription)
-> IO
     ([PWarning],
      Either (Maybe Version, NonEmpty PError) GenericPackageDescription)
forall a b. (a -> b) -> a -> b
$ ParseResult GenericPackageDescription
-> ([PWarning],
    Either (Maybe Version, NonEmpty PError) GenericPackageDescription)
forall a.
ParseResult a
-> ([PWarning], Either (Maybe Version, NonEmpty PError) a)
runParseResult (ByteString -> ParseResult GenericPackageDescription
parseGenericPackageDescription ByteString
bs)

readCabalFields ::
  NormalizedFilePath ->
  BS.ByteString ->
  Either FileDiagnostic [Syntax.Field Syntax.Position]
readCabalFields :: NormalizedFilePath
-> ByteString -> Either FileDiagnostic [Field Position]
readCabalFields NormalizedFilePath
file ByteString
contents  = do
  case ByteString -> Either ParseError ([Field Position], [LexWarning])
Syntax.readFields' ByteString
contents of
    Left ParseError
parseError ->
      FileDiagnostic -> Either FileDiagnostic [Field Position]
forall a b. a -> Either a b
Left (FileDiagnostic -> Either FileDiagnostic [Field Position])
-> FileDiagnostic -> Either FileDiagnostic [Field Position]
forall a b. (a -> b) -> a -> b
$ NormalizedFilePath -> Text -> FileDiagnostic
Diagnostics.fatalParseErrorDiagnostic NormalizedFilePath
file
           (Text -> FileDiagnostic) -> Text -> FileDiagnostic
forall a b. (a -> b) -> a -> b
$ Text
"Failed to parse cabal file: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
T.pack (ParseError -> String
forall a. Show a => a -> String
show ParseError
parseError)
    Right ([Field Position]
fields, [LexWarning]
_warnings) -> do
      -- we don't want to double report diagnostics, all diagnostics are produced by 'ParseCabalFile'.
      [Field Position] -> Either FileDiagnostic [Field Position]
forall a b. b -> Either a b
Right [Field Position]
fields