{-# LANGUAGE RecordWildCards #-}
module System.Console.CmdArgs.Explicit.ExpandArgsAt(expandArgsAt) where

import System.FilePath


-- | Expand @\@@ directives in a list of arguments, usually obtained from 'getArgs'.
--   As an example, given the file @test.txt@ with the lines @hello@ and @world@:
--
-- > expandArgsAt ["@test.txt","!"] == ["hello","world","!"]
--
--   Any @\@@ directives in the files will be recursively expanded (raising an error
--   if there is infinite recursion).
--
--   To supress @\@@ expansion, pass any @\@@ arguments after @--@.
expandArgsAt :: [String] -> IO [String]
expandArgsAt :: [String] -> IO [String]
expandArgsAt [String]
args = do
        [[String]]
ebefore <- (String -> IO [String]) -> [String] -> IO [[String]]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM ([String] -> String -> String -> IO [String]
f [] String
".") [String]
before
        [String] -> IO [String]
forall (m :: * -> *) a. Monad m => a -> m a
return ([String] -> IO [String]) -> [String] -> IO [String]
forall a b. (a -> b) -> a -> b
$ [[String]] -> [String]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[String]]
ebefore [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String]
after
    where
        ([String]
before,[String]
after) = (String -> Bool) -> [String] -> ([String], [String])
forall a. (a -> Bool) -> [a] -> ([a], [a])
break (String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"--") [String]
args

        f :: [String] -> String -> String -> IO [String]
f [String]
seen String
dir (Char
'@':String
x)
            | String
x String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
seen = String -> IO [String]
forall a. HasCallStack => String -> a
error (String -> IO [String]) -> String -> IO [String]
forall a b. (a -> b) -> a -> b
$ [String] -> String
unlines ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$
                String
"System.Console.CmdArgs.Explicit.expandArgsAt, recursion in @ directives:" String -> [String] -> [String]
forall a. a -> [a] -> [a]
:
                (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String
"  "String -> String -> String
forall a. [a] -> [a] -> [a]
++) ([String] -> [String]
forall a. [a] -> [a]
reverse ([String] -> [String]) -> [String] -> [String]
forall a b. (a -> b) -> a -> b
$ String
xString -> [String] -> [String]
forall a. a -> [a] -> [a]
:[String]
seen)
            | [String] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [String]
seen Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
15 = String -> IO [String]
forall a. HasCallStack => String -> a
error (String -> IO [String]) -> String -> IO [String]
forall a b. (a -> b) -> a -> b
$ [String] -> String
unlines ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$
                String
"System.Console.CmdArgs.Explicit.expandArgsAt, over 15 @ directives deep:" String -> [String] -> [String]
forall a. a -> [a] -> [a]
:
                (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String
"  "String -> String -> String
forall a. [a] -> [a] -> [a]
++) ([String] -> [String]
forall a. [a] -> [a]
reverse [String]
seen)
            | Bool
otherwise = do
                String
src <- String -> IO String
readFile (String -> IO String) -> String -> IO String
forall a b. (a -> b) -> a -> b
$ String
dir String -> String -> String
</> String
x
                ([[String]] -> [String]) -> IO [[String]] -> IO [String]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [[String]] -> [String]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat (IO [[String]] -> IO [String]) -> IO [[String]] -> IO [String]
forall a b. (a -> b) -> a -> b
$ (String -> IO [String]) -> [String] -> IO [[String]]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM ([String] -> String -> String -> IO [String]
f (String
xString -> [String] -> [String]
forall a. a -> [a] -> [a]
:[String]
seen) (String -> String
takeDirectory String
x)) ([String] -> IO [[String]]) -> [String] -> IO [[String]]
forall a b. (a -> b) -> a -> b
$ String -> [String]
lines String
src
        f [String]
_ String
_ String
x = [String] -> IO [String]
forall (m :: * -> *) a. Monad m => a -> m a
return [String
x]