cmdtheline-0.1.0.0: doc/examples/cp.hs
import System.Console.CmdTheLine
import Control.Applicative
import System.Directory ( copyFile
, doesFileExist
, doesDirectoryExist
)
import System.FilePath ( takeFileName
, pathSeparator
, hasTrailingPathSeparator
)
import System.IO
import System.Exit ( exitFailure )
sep = [pathSeparator]
infixr 1 ?
-- Like C's ternary operator. 'predicate ? then-clause $ else-clause'.
-- Nice for nested, if-elseif style boolean bifurcation.
(?) True = const
(?) False = flip const
cp :: Bool -> [String] -> String -> IO ()
cp dry sources dest =
chooseTactic =<< doesDirectoryExist dest
where
chooseTactic isDir =
singleFile ? singleCopy isDir $
not isDir ? notDirErr $
mapM_ copySourcesToDir sources
singleFile = length sources == 1
-- Errors
notDirErr = do
hPutStrLn stderr $ "cp: target '" ++ dest ++ "' is not a directory"
exitFailure
notFileErr str = do
hPutStrLn stderr $ "cp: '" ++ str ++ "': no such file"
exitFailure
-- Tactics
singleCopy isDir = do
choose =<< doesFileExist filePath
where
choose isFile =
isFile && isDir ? copyToDir filePath $
isFile ? copyToFile filePath $
notFileErr filePath
filePath = head sources
copySourcesToDir filePath = do
isFile <- doesFileExist filePath
isFile ? copyToDir filePath
$ notFileErr filePath
-- File copying
copyToDir filePath = if dry
then putStrLn $ concat [ "cp: copying ", filePath, " to ", dest' ]
else copyFile filePath dest'
where
dest' = withTrailingSep ++ takeFileName filePath
withTrailingSep = hasTrailingPathSeparator dest ? dest $ dest ++ sep
copyToFile filePath = if dry
then putStrLn $ concat [ "cp: copying ", filePath, " to ", dest ]
else copyFile filePath dest
-- An example of using the 'rev' and 'Left' variants of 'pos'.
cpTerm = cp <$> dry <*> sources <*> dest
where
dry = flag (optInfo [ "dry", "d" ])
{ argName = "DRY"
, argDoc = "Perform a dry run. Print what would be copied, but do not "
++ "copy it."
}
sources = nonEmpty $ revPosLeft 0 [] posInfo
{ argName = "SOURCES"
, argDoc = "Source file(s) to copy."
}
dest = required $ revPos 0 Nothing posInfo
{ argName = "DEST"
, argDoc = "Destination of the copy. Must be a directory if there "
++ "is more than one $(i,SOURCE)."
}
termInfo = def
{ termName = "cp"
, version = "v1.0"
, termDoc = "Copy files from SOURCES to DEST."
, man = [ S "BUGS"
, P "Email bug reports to <portManTwo@example.com>"
]
}
main = run ( cpTerm, termInfo )