summaryrefslogtreecommitdiff
path: root/CmdLine.hs
diff options
context:
space:
mode:
authorGravatar Joey Hess <joey@kitenet.net>2010-11-02 19:04:24 -0400
committerGravatar Joey Hess <joey@kitenet.net>2010-11-02 19:04:24 -0400
commit0eae5b806c76b0fa3e21fbae6e5f2d9a39a04cce (patch)
tree53aada39ec10bc6217507bce1a9add3b86b3793b /CmdLine.hs
parent606ed6bb3566fa86c1783e3f1c7d799a6f1be8d1 (diff)
broke subcommands out into separate modules
Diffstat (limited to 'CmdLine.hs')
-rw-r--r--CmdLine.hs201
1 files changed, 201 insertions, 0 deletions
diff --git a/CmdLine.hs b/CmdLine.hs
new file mode 100644
index 000000000..494da2873
--- /dev/null
+++ b/CmdLine.hs
@@ -0,0 +1,201 @@
+{- git-annex command line
+ -
+ - Copyright 2010 Joey Hess <joey@kitenet.net>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+module CmdLine (parseCmd) where
+
+import System.Console.GetOpt
+import Control.Monad.State (liftIO)
+import System.Directory
+import Data.String.Utils
+import Control.Monad (filterM)
+import Monad (when)
+
+import qualified GitRepo as Git
+import qualified Annex
+import Locations
+import qualified Backend
+import Types
+import Core
+
+import Command
+import qualified Command.Add
+import qualified Command.Unannex
+import qualified Command.Drop
+import qualified Command.Move
+import qualified Command.Get
+import qualified Command.FromKey
+import qualified Command.DropKey
+import qualified Command.SetKey
+import qualified Command.Fix
+import qualified Command.Init
+
+data SubCommand = SubCommand {
+ subcmdname :: String,
+ subcmdparams :: String,
+ subcmdseek :: SubCmdSeek,
+ subcmddesc :: String
+}
+subCmds :: [SubCommand]
+subCmds = [
+ (SubCommand "add" path (withFilesNotInGit Command.Add.start)
+ "add files to annex")
+ , (SubCommand "get" path (withFilesInGit Command.Get.start)
+ "make content of annexed files available")
+ , (SubCommand "drop" path (withFilesInGit Command.Drop.start)
+ "indicate content of files not currently wanted")
+ , (SubCommand "move" path (withFilesInGit Command.Move.start)
+ "transfer content of files to/from another repository")
+ , (SubCommand "init" desc (withDescription Command.Init.start)
+ "initialize git-annex with repository description")
+ , (SubCommand "unannex" path (withFilesInGit Command.Unannex.start)
+ "undo accidential add command")
+ , (SubCommand "fix" path (withFilesInGit Command.Fix.start)
+ "fix up symlinks to point to annexed content")
+ , (SubCommand "pre-commit" path (withFilesToBeCommitted Command.Fix.start)
+ "fix up symlinks before they are committed")
+ , (SubCommand "fromkey" key (withFilesMissing Command.FromKey.start)
+ "adds a file using a specific key")
+ , (SubCommand "dropkey" key (withKeys Command.DropKey.start)
+ "drops annexed content for specified keys")
+ , (SubCommand "setkey" key (withTempFile Command.SetKey.start)
+ "sets annexed content for a key using a temp file")
+ ]
+ where
+ path = "PATH ..."
+ key = "KEY ..."
+ desc = "DESCRIPTION"
+
+-- Each dashed command-line option results in generation of an action
+-- in the Annex monad that performs the necessary setting.
+options :: [OptDescr (Annex ())]
+options = [
+ Option ['f'] ["force"] (NoArg (storebool "force" True))
+ "allow actions that may lose annexed data"
+ , Option ['q'] ["quiet"] (NoArg (storebool "quiet" True))
+ "avoid verbose output"
+ , Option ['v'] ["verbose"] (NoArg (storebool "quiet" False))
+ "allow verbose output"
+ , Option ['b'] ["backend"] (ReqArg (storestring "backend") "NAME")
+ "specify default key-value backend to use"
+ , Option ['k'] ["key"] (ReqArg (storestring "key") "KEY")
+ "specify a key to use"
+ , Option ['t'] ["to"] (ReqArg (storestring "torepository") "REPOSITORY")
+ "specify to where to transfer content"
+ , Option ['f'] ["from"] (ReqArg (storestring "fromrepository") "REPOSITORY")
+ "specify from where to transfer content"
+ ]
+ where
+ storebool n b = Annex.flagChange n $ FlagBool b
+ storestring n s = Annex.flagChange n $ FlagString s
+
+header :: String
+header = "Usage: git-annex " ++ (join "|" $ map subcmdname subCmds)
+
+{- Usage message with lists of options and subcommands. -}
+usage :: String
+usage = usageInfo header options ++ "\nSubcommands:\n" ++ cmddescs
+ where
+ cmddescs = unlines $ map (\c -> indent $ showcmd c) subCmds
+ showcmd c =
+ (subcmdname c) ++
+ (pad 11 (subcmdname c)) ++
+ (subcmdparams c) ++
+ (pad 13 (subcmdparams c)) ++
+ (subcmddesc c)
+ indent l = " " ++ l
+ pad n s = take (n - (length s)) $ repeat ' '
+
+{- Prepares a list of actions to run to perform a subcommand, based on
+ - the parameters passed to it. -}
+prepSubCmd :: SubCommand -> AnnexState -> [String] -> IO [Annex Bool]
+prepSubCmd SubCommand { subcmdseek = seek } state params = do
+ list <- Annex.eval state $ seek params
+ return $ map (\a -> doSubCmd a) list
+
+{- Runs a subcommand through the start, perform and cleanup stages -}
+doSubCmd :: SubCmdStart -> SubCmdCleanup
+doSubCmd start = do
+ s <- start
+ case (s) of
+ Nothing -> return True
+ Just perform -> do
+ p <- perform
+ case (p) of
+ Nothing -> do
+ showEndFail
+ return False
+ Just cleanup -> do
+ c <- cleanup
+ if (c)
+ then do
+ showEndOk
+ return True
+ else do
+ showEndFail
+ return False
+
+{- These functions find appropriate files or other things based on a
+ user's parameters. -}
+withFilesNotInGit :: SubCmdSeekBackendFiles
+withFilesNotInGit a params = do
+ repo <- Annex.gitRepo
+ files <- liftIO $ mapM (Git.notInRepo repo) params
+ let files' = foldl (++) [] files
+ pairs <- Backend.chooseBackends files'
+ return $ map a $ filter (\(f,_) -> notState f) pairs
+withFilesInGit :: SubCmdSeekStrings
+withFilesInGit a params = do
+ repo <- Annex.gitRepo
+ files <- liftIO $ mapM (Git.inRepo repo) params
+ return $ map a $ filter notState $ foldl (++) [] files
+withFilesMissing :: SubCmdSeekStrings
+withFilesMissing a params = do
+ files <- liftIO $ filterM missing params
+ return $ map a $ filter notState files
+ where
+ missing f = do
+ e <- doesFileExist f
+ return $ not e
+withDescription :: SubCmdSeekStrings
+withDescription a params = do
+ return $ [a $ unwords params]
+withFilesToBeCommitted :: SubCmdSeekStrings
+withFilesToBeCommitted a params = do
+ repo <- Annex.gitRepo
+ files <- liftIO $ mapM (Git.stagedFiles repo) params
+ return $ map a $ filter notState $ foldl (++) [] files
+withKeys :: SubCmdSeekStrings
+withKeys a params = return $ map a params
+withTempFile :: SubCmdSeekStrings
+withTempFile a params = return $ map a params
+
+{- filter out files from the state directory -}
+notState :: FilePath -> Bool
+notState f = stateLoc /= take (length stateLoc) f
+
+{- Parses command line and returns two lists of actions to be
+ - run in the Annex monad. The first actions configure it
+ - according to command line options, while the second actions
+ - handle subcommands. -}
+parseCmd :: [String] -> AnnexState -> IO ([Annex Bool], [Annex Bool])
+parseCmd argv state = do
+ (flags, params) <- getopt
+ when (null params) $ error usage
+ case lookupCmd (params !! 0) of
+ [] -> error usage
+ [subcommand] -> do
+ actions <- prepSubCmd subcommand state (drop 1 params)
+ let configactions = map (\flag -> do
+ flag
+ return True) flags
+ return (configactions, actions)
+ _ -> error "internal error: multiple matching subcommands"
+ where
+ getopt = case getOpt Permute options argv of
+ (flags, params, []) -> return (flags, params)
+ (_, _, errs) -> ioError (userError (concat errs ++ usage))
+ lookupCmd cmd = filter (\c -> cmd == subcmdname c) subCmds