module Portage.Host
  ( getInfo -- :: IO [(String, String)]
  , LocalInfo(..)
  ) where

import Util (run_cmd)
import qualified Data.List.Split as DLS
import Data.Maybe (fromJust, isJust, mapMaybe)

import qualified System.Directory as D
import           System.FilePath ((</>))

import System.IO

data LocalInfo =
    LocalInfo { LocalInfo -> String
distfiles_dir :: String
              , LocalInfo -> [String]
overlay_list  :: [FilePath]
              , LocalInfo -> String
portage_dir   :: FilePath
              } deriving (ReadPrec [LocalInfo]
ReadPrec LocalInfo
Int -> ReadS LocalInfo
ReadS [LocalInfo]
(Int -> ReadS LocalInfo)
-> ReadS [LocalInfo]
-> ReadPrec LocalInfo
-> ReadPrec [LocalInfo]
-> Read LocalInfo
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [LocalInfo]
$creadListPrec :: ReadPrec [LocalInfo]
readPrec :: ReadPrec LocalInfo
$creadPrec :: ReadPrec LocalInfo
readList :: ReadS [LocalInfo]
$creadList :: ReadS [LocalInfo]
readsPrec :: Int -> ReadS LocalInfo
$creadsPrec :: Int -> ReadS LocalInfo
Read, Int -> LocalInfo -> ShowS
[LocalInfo] -> ShowS
LocalInfo -> String
(Int -> LocalInfo -> ShowS)
-> (LocalInfo -> String)
-> ([LocalInfo] -> ShowS)
-> Show LocalInfo
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [LocalInfo] -> ShowS
$cshowList :: [LocalInfo] -> ShowS
show :: LocalInfo -> String
$cshow :: LocalInfo -> String
showsPrec :: Int -> LocalInfo -> ShowS
$cshowsPrec :: Int -> LocalInfo -> ShowS
Show)

defaultInfo :: LocalInfo
defaultInfo :: LocalInfo
defaultInfo = LocalInfo :: String -> [String] -> String -> LocalInfo
LocalInfo { distfiles_dir :: String
distfiles_dir = String
"/usr/portage/distfiles"
                        , overlay_list :: [String]
overlay_list  = []
                        , portage_dir :: String
portage_dir   = String
"/usr/portage"
                        }

-- query paludis and then emerge
getInfo :: IO LocalInfo
getInfo :: IO LocalInfo
getInfo = Maybe LocalInfo -> LocalInfo
forall a. HasCallStack => Maybe a -> a
fromJust (Maybe LocalInfo -> LocalInfo)
-> IO (Maybe LocalInfo) -> IO LocalInfo
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap`
    [IO (Maybe LocalInfo)] -> IO (Maybe LocalInfo)
forall (m :: * -> *) a. Monad m => [m (Maybe a)] -> m (Maybe a)
performMaybes [ IO (Maybe LocalInfo)
readConfig
                  , [IO (Maybe LocalInfo)] -> IO (Maybe LocalInfo)
forall (m :: * -> *) a. Monad m => [m (Maybe a)] -> m (Maybe a)
performMaybes [ IO (Maybe LocalInfo)
getPaludisInfo
                                  , IO (Maybe LocalInfo)
askPortageq
                                  , Maybe LocalInfo -> IO (Maybe LocalInfo)
forall (m :: * -> *) a. Monad m => a -> m a
return (LocalInfo -> Maybe LocalInfo
forall a. a -> Maybe a
Just LocalInfo
defaultInfo)
                                  ] IO (Maybe LocalInfo)
-> (Maybe LocalInfo -> IO (Maybe LocalInfo))
-> IO (Maybe LocalInfo)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Maybe LocalInfo -> IO (Maybe LocalInfo)
showAnnoyingWarning
                  ]
    where performMaybes :: [m (Maybe a)] -> m (Maybe a)
performMaybes [] = Maybe a -> m (Maybe a)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe a
forall a. Maybe a
Nothing
          performMaybes (m (Maybe a)
act:[m (Maybe a)]
acts) =
              do Maybe a
r <- m (Maybe a)
act
                 if Maybe a -> Bool
forall a. Maybe a -> Bool
isJust Maybe a
r
                     then Maybe a -> m (Maybe a)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe a
r
                     else [m (Maybe a)] -> m (Maybe a)
performMaybes [m (Maybe a)]
acts

showAnnoyingWarning :: Maybe LocalInfo -> IO (Maybe LocalInfo)
showAnnoyingWarning :: Maybe LocalInfo -> IO (Maybe LocalInfo)
showAnnoyingWarning Maybe LocalInfo
info = do
    Handle -> String -> IO ()
hPutStr Handle
stderr (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ [String] -> String
unlines [ String
"-- Consider creating ~/" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
hackport_config String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" file with contents:"
                             , Maybe LocalInfo -> String
forall a. Show a => a -> String
show Maybe LocalInfo
info
                             , String
"-- It will speed hackport startup time a bit."
                             ]
    Maybe LocalInfo -> IO (Maybe LocalInfo)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe LocalInfo
info

-- relative to home dir
hackport_config :: FilePath
hackport_config :: String
hackport_config = String
".hackport" String -> ShowS
</> String
"repositories"

--------------------------
-- fastest: config reading
--------------------------
readConfig :: IO (Maybe LocalInfo)
readConfig :: IO (Maybe LocalInfo)
readConfig =
    do String
home_dir <- IO String
D.getHomeDirectory
       let config_path :: String
config_path  = String
home_dir String -> ShowS
</> String
hackport_config
       Bool
exists <- String -> IO Bool
D.doesFileExist String
config_path
       if Bool
exists then String -> Maybe LocalInfo
forall a. Read a => String -> a
read (String -> Maybe LocalInfo) -> IO String -> IO (Maybe LocalInfo)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> IO String
readFile String
config_path else Maybe LocalInfo -> IO (Maybe LocalInfo)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe LocalInfo
forall a. Maybe a
Nothing

----------
-- Paludis
----------

getPaludisInfo :: IO (Maybe LocalInfo)
getPaludisInfo :: IO (Maybe LocalInfo)
getPaludisInfo = (String -> LocalInfo) -> Maybe String -> Maybe LocalInfo
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap String -> LocalInfo
parsePaludisInfo (Maybe String -> Maybe LocalInfo)
-> IO (Maybe String) -> IO (Maybe LocalInfo)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> IO (Maybe String)
run_cmd String
"cave info"

parsePaludisInfo :: String -> LocalInfo
parsePaludisInfo :: String -> LocalInfo
parsePaludisInfo String
text =
  let chunks :: [[String]]
chunks = [String] -> [String] -> [[String]]
forall a. Eq a => [a] -> [a] -> [[a]]
DLS.splitOn [String
""] ([String] -> [[String]])
-> (String -> [String]) -> String -> [[String]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [String]
lines (String -> [[String]]) -> String -> [[String]]
forall a b. (a -> b) -> a -> b
$ String
text
      repositories :: [(String, (String, String))]
repositories = ([String] -> Maybe (String, (String, String)))
-> [[String]] -> [(String, (String, String))]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe [String] -> Maybe (String, (String, String))
parseRepository [[String]]
chunks
  in  Maybe LocalInfo -> LocalInfo
forall a. HasCallStack => Maybe a -> a
fromJust ([(String, (String, String))] -> Maybe LocalInfo
mkLocalInfo [(String, (String, String))]
repositories)
  where
  parseRepository :: [String] -> Maybe (String, (String, String))
  parseRepository :: [String] -> Maybe (String, (String, String))
parseRepository [] = Maybe (String, (String, String))
forall a. Maybe a
Nothing
  parseRepository (String
firstLine:[String]
lns) = do
    String
name <- case String -> [String]
words String
firstLine of
                [String
"Repository", String
nm] -> String -> Maybe String
forall (m :: * -> *) a. Monad m => a -> m a
return (ShowS
forall a. [a] -> [a]
init String
nm)
                [String]
_ -> String -> Maybe String
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"not a repository chunk"
    let dict :: [(String, String)]
dict = [ ([String] -> String
forall a. [a] -> a
head [String]
ln, [String] -> String
unwords ([String] -> [String]
forall a. [a] -> [a]
tail [String]
ln)) | [String]
ln <- (String -> [String]) -> [String] -> [[String]]
forall a b. (a -> b) -> [a] -> [b]
map String -> [String]
words [String]
lns ]
    String
location <- String -> [(String, String)] -> Maybe String
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup String
"location" [(String, String)]
dict
    String
distfiles <- String -> [(String, String)] -> Maybe String
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup String
"distdir" [(String, String)]
dict
    (String, (String, String)) -> Maybe (String, (String, String))
forall (m :: * -> *) a. Monad m => a -> m a
return (String
name, (String
location, String
distfiles))

  mkLocalInfo :: [(String, (String, String))] -> Maybe LocalInfo
  mkLocalInfo :: [(String, (String, String))] -> Maybe LocalInfo
mkLocalInfo [(String, (String, String))]
repos = do
    (String
gentooLocation, String
gentooDistfiles) <- String -> [(String, (String, String))] -> Maybe (String, String)
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup String
"gentoo" [(String, (String, String))]
repos
    let overlays :: [String]
overlays = [ String
loc | (String
_, (String
loc, String
_dist)) <- [(String, (String, String))]
repos ]
    LocalInfo -> Maybe LocalInfo
forall (m :: * -> *) a. Monad m => a -> m a
return (LocalInfo :: String -> [String] -> String -> LocalInfo
LocalInfo
              { distfiles_dir :: String
distfiles_dir = String
gentooDistfiles
              , portage_dir :: String
portage_dir = String
gentooLocation
              , overlay_list :: [String]
overlay_list = [String]
overlays
              })

---------
-- Emerge
---------

askPortageq :: IO (Maybe LocalInfo)
askPortageq :: IO (Maybe LocalInfo)
askPortageq = do
    Maybe String
distdir <- String -> IO (Maybe String)
run_cmd String
"portageq distdir"
    Maybe String
portdir <- String -> IO (Maybe String)
run_cmd String
"portageq get_repo_path / gentoo"
    Maybe String
hsRepo  <- String -> IO (Maybe String)
run_cmd String
"portageq get_repo_path / haskell"
    --There really ought to be both distdir and portdir,
    --but maybe no hsRepo defined yet.
    let info :: Maybe LocalInfo
info = if Maybe String
forall a. Maybe a
Nothing Maybe String -> [Maybe String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Maybe String
distdir,Maybe String
portdir]
               then Maybe LocalInfo
forall a. Maybe a
Nothing
               else LocalInfo -> Maybe LocalInfo
forall a. a -> Maybe a
Just LocalInfo :: String -> [String] -> String -> LocalInfo
LocalInfo
                      { distfiles_dir :: String
distfiles_dir = Maybe String -> String
forall a. Maybe [a] -> [a]
grab Maybe String
distdir
                      , portage_dir :: String
portage_dir = Maybe String -> String
forall a. Maybe [a] -> [a]
grab Maybe String
portdir
                      , overlay_list :: [String]
overlay_list = Maybe String -> [String]
forall a. Maybe [a] -> [[a]]
iffy Maybe String
hsRepo
                      }
                 --init: kill newline char
                 where grab :: Maybe [a] -> [a]
grab = [a] -> [a]
forall a. [a] -> [a]
init ([a] -> [a]) -> (Maybe [a] -> [a]) -> Maybe [a] -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe [a] -> [a]
forall a. HasCallStack => Maybe a -> a
fromJust
                       iffy :: Maybe [a] -> [[a]]
iffy Maybe [a]
Nothing     = []
                       iffy (Just [a]
repo) = [[a] -> [a]
forall a. [a] -> [a]
init [a]
repo]
    Maybe LocalInfo -> IO (Maybe LocalInfo)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe LocalInfo
info