packages feed

leksah-server-0.16.2.0: src/IDE/Utils/CabalProject.hs

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE LambdaCase #-}
-----------------------------------------------------------------------------
--
-- Module      :  IDE.Utils.CabalProject
-- Copyright   :  2016-2016 Juergen Nicklisch-Franken, Hamish Mackenzie
-- License     :  GPL
--
-- Maintainer  :  maintainer@leksah.org
-- Stability   :  provisional
-- Portability :
--
-- |
--
-----------------------------------------------------------------------------

module IDE.Utils.CabalProject (
    findProjectRoot
  , projectPackages
  , getCabalProjectPackages
) where

import Prelude hiding (readFile)
import System.Directory (doesFileExist, getHomeDirectory)
import System.FilePath (takeDirectory, isDrive, (</>))
import Control.Exception (catch, SomeException(..))
import System.IO.Strict (readFile)

findProjectRoot :: FilePath -> IO FilePath
findProjectRoot curdir = do

    -- Copied from cabal-install as it is not exposed
    -- and until features like `cabal run` exist for `cabal new-build`
    homedir <- getHomeDirectory

    -- Search upwards. If we get to the users home dir or the filesystem root,
    -- then use the current dir
    let probe dir | isDrive dir || dir == homedir
                  = return curdir -- implicit project root
        probe dir = do
          exists <- doesFileExist (dir </> "cabal.project")
          if exists
            then return dir       -- explicit project root
            else probe (takeDirectory dir)

    probe curdir
   --TODO: [nice to have] add compat support for old style sandboxes

-- |
-- >>> :{
--  projectPackages $ unlines
--    [ "something befor"
--    , "--comment"
--    , "packages:"
--    , "--comment2"
--    , " package1"
--    , ""
--    , " "
--    , " --comment3"
--    , " package2 --comment4"
--    , "notpackage"
--    , " also notpackage" ]
-- :}
-- ["package1","package2"]
projectPackages :: String -> [FilePath]
projectPackages = map (dropWhile (==' ')) .
                  takeWhile (not . isNewSection) .
                  drop 1 .
                  dropWhile (/="packages:") .
                  filter (not . null) .
                  map (trimTrailing . dropComment) .
                  lines
  where
    dropComment ('-':'-':_) = ""
    dropComment (x:xs) = x:dropComment xs
    dropComment "" = ""

    trimTrailing = reverse . dropWhile (==' ') . reverse

    isNewSection "" = False
    isNewSection (' ':_) = False
    isNewSection _ = True

getCabalProjectPackages :: FilePath -> IO [FilePath]
getCabalProjectPackages dir = do
    projectRoot <- findProjectRoot dir
    let projectFile = projectRoot </> "cabal.project"
    doesFileExist projectFile >>= \case
        False -> return []
        True  -> (map (projectRoot </>) . projectPackages <$> readFile projectFile)
                    `catch` (\(_ :: SomeException) -> return [])