%  Copyright (C) 2007 Kevin Quick
%
%  This program is free software; you can redistribute it and/or modify
%  it under the terms of the GNU General Public License as published by
%  the Free Software Foundation; either version 2, or (at your option)
%  any later version.
%
%  This program is distributed in the hope that it will be useful,
%  but WITHOUT ANY WARRANTY; without even the implied warranty of
%  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%  GNU General Public License for more details.
%
%  You should have received a copy of the GNU General Public License
%  along with this program; see the file COPYING.  If not, write to
%  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
%  Boston, MA 02110-1301, USA.

\subsubsection{darcs show repo}
%%\label{show-repo}

\options{show repo}

The \verb!show repo! displays information about
the current repository: the location, the type, etc.

This is provided as informational output for two purposes: curious
users and scripts invoking darcs.  For the latter, this information
can be parsed to facilitate the script; for example,
\verb!darcs show repo | grep Root: | awk {print $2}!
can be used to locate the
top-level \verb!_darcs! directory from anyplace within a darcs repository
working directory.

\begin{code}
{-# OPTIONS_GHC -cpp #-}
{-# LANGUAGE CPP #-}
#include "gadts.h"
module Darcs.Commands.ShowRepo ( show_repo ) where

import Data.Char ( toLower, isSpace )
import Data.List ( intersperse )
import Control.Monad ( when, unless )
import Text.Html ( tag, stringToHtml )
import Darcs.Arguments ( DarcsFlag(..), working_repo_dir, files, xmloutput )
import Darcs.Commands ( DarcsCommand(..), nodefaults )
import Darcs.Repository ( withRepository, ($-), amInRepository, read_repo )
import Darcs.Repository.Internal ( Repository(..), RepoType(..) )
import Darcs.Repository.Format ( RepoFormat(..) )
import Darcs.Repository.Prefs ( get_preflist )
import Darcs.Repository.Motd ( get_motd )
import Darcs.Global ( darcsdir )
import Darcs.Patch ( RepoPatch )
import Darcs.Ordered ( lengthRL, concatRL )
import qualified Data.ByteString.Char8 as BC  (unpack)

show_repo_help :: String
show_repo_help =
 "The repo command displays information about the current repository\n" ++
 "(location, type, etc.).  Some of this information is already available\n" ++
 "by inspecting files within the "++darcsdir++" directory and some is internal\n" ++
 "information that is informational only (i.e. for developers).  This\n" ++
 "command collects all of the repository information into a readily\n" ++
 "available source.\n"

show_repo_description :: String
show_repo_description = "Show repository summary information"

show_repo :: DarcsCommand
show_repo = DarcsCommand { command_name = "repo",
                           command_help = show_repo_help,
                           command_description = show_repo_description,
                           command_extra_args = 0,
                           command_extra_arg_help = [],
                           command_command = repo_cmd,
                           command_prereq = amInRepository,
                           command_get_arg_possibilities = return [],
                           command_argdefaults = nodefaults,
                           command_advanced_options = [],
                           command_basic_options = [working_repo_dir, files, xmloutput] }
\end{code}

\begin{options}
--files, --no-files
\end{options}

If the \verb!--files! option is specified (the default), then the
\verb!show repo! operation will read patch information from the
repository and display the number of patches in the repository.  The
\verb!--no-files! option can be used to suppress this operation (and
improve performance).

\begin{code}
repo_cmd :: [DarcsFlag] -> [String] -> IO ()
repo_cmd opts _ = let put_mode = if XMLOutput `elem` opts then showInfoXML else showInfoUsr
                  in withRepository opts $- \repository -> showRepo (putInfo put_mode) repository
\end{code}

\begin{options}
--human-readable, --xml-output
\end{options}

By default, the \verb!show repo! displays output in human readable
form, but the \verb!--xml-output! option can be used to obtain
XML-formatted to facilitate regular parsing by external tools.

\begin{code}
-- Some convenience functions to output a labelled text string or an
-- XML tag + value (same API).  If no value, output is suppressed
-- entirely.  Borrow some help from Text.Html to perform XML output.

type ShowInfo = String -> String -> String

showInfoXML :: ShowInfo
showInfoXML t i = show $ tag (safeTag t) $ stringToHtml i

safeTag :: String -> String
safeTag [] = []
safeTag (' ':cs) = safeTag cs
safeTag ('#':cs) = "num_" ++ (safeTag cs)
safeTag (c:cs) = toLower c : safeTag cs

-- labelled strings: labels are right-aligned at 14 characters;
-- subsequent lines in multi-line output are indented accordingly.
showInfoUsr :: ShowInfo
showInfoUsr t i = (replicate (14 - length(t)) ' ') ++ t ++ ": " ++
                  (concat $ intersperse ('\n' : (replicate 16 ' ')) $ lines i) ++ "\n"

type PutInfo = String -> String -> IO ()
putInfo :: ShowInfo -> PutInfo
putInfo m t i = unless (null i) (putStr $ m t i)

-- Primary show-repo operation.  Determines ordering of output for
-- sub-displays.  The `out' argument is one of the above operations to
-- output a labelled text string or an XML tag and contained value.

showRepo :: RepoPatch p => PutInfo -> Repository p C(r u r) -> IO ()
showRepo out r@(Repo loc opts rf rt) = do
         when (XMLOutput `elem` opts) (putStr "<repository>\n")
         showRepoType out rt
         when (Verbose `elem` opts) (out "Show" $ show r)
         showRepoFormat out rf
         out "Root" loc
         showRepoAux out rt
         showRepoPrefs out
         unless (NoFiles `elem` opts) (numPatches r >>= (out "Num Patches" . show ))
         showRepoMOTD out r
         when (XMLOutput `elem` opts) (putStr "</repository>\n")

-- Most of the actual elements being displayed are part of the Show
-- class; that's fine for a Haskeller, but not for the common user, so
-- the routines below work to provide more human-readable information
-- regarding the repository elements.

showRepoType :: PutInfo -> RepoType p -> IO ()
showRepoType out (DarcsRepository _ _) = out "Type" "darcs"

showRepoFormat :: PutInfo -> RepoFormat -> IO ()
showRepoFormat out (RF rf) = out "Format" $
    concat $ intersperse ", " (map (concat . intersperse "|" . map BC.unpack) rf)

showRepoAux :: PutInfo -> RepoType p -> IO ()
showRepoAux out (DarcsRepository pris cs) =
    do out "Pristine" $ show pris
       out "Cache" $ concat $ intersperse ", " $ lines $ show cs


showRepoPrefs :: PutInfo -> IO ()
showRepoPrefs out = do
    get_preflist "prefs" >>= mapM_ prefOut
    get_preflist "author" >>= out "Author" . unlines
    get_preflist "defaultrepo" >>= out "Default Remote" . unlines
  where prefOut = uncurry out . (\(p,v) -> (p++" Pref", (dropWhile isSpace v))) . break isSpace

showRepoMOTD :: RepoPatch p => PutInfo -> Repository p C(r u r) -> IO ()
showRepoMOTD out (Repo loc _ _ _) = get_motd loc >>= out "MOTD" . BC.unpack

-- Support routines to provide information used by the PutInfo operations above.

numPatches :: RepoPatch p => Repository p C(r u r) -> IO Int
numPatches r = read_repo r >>= (return . lengthRL . concatRL)

\end{code}