darcsden-1.1: src/DarcsDen/Handler/Repository/Browse.hs
{-# LANGUAGE DeriveDataTypeable, GADTs, CPP #-}
module DarcsDen.Handler.Repository.Browse where
#ifdef DARCS28
import Darcs.Utils (withCurrentDirectory)
#else
import Darcs.Util.File (withCurrentDirectory)
#endif
import Data.Char (toLower)
import Data.Data (Data)
import Data.List (intercalate, isPrefixOf, isSuffixOf, sort)
import Data.Maybe (isJust, listToMaybe)
import Data.Typeable (Typeable)
import qualified Darcs.Patch as P
import Darcs.Patch.Apply ( ApplyState )
import qualified Darcs.Repository as R
import qualified Darcs.Repository.InternalTypes as RI
import qualified Data.ByteString.Lazy as LBS
import qualified Storage.Hashed.Tree as T
import qualified Storage.Hashed.AnchoredPath as A
import DarcsDen.Handler.Repository.Util
import DarcsDen.Settings
import DarcsDen.Util
data RepoItem =
RepoItem
{ iName :: String
, iPath :: String -- relative to the repo url, with leading slash.
, iIsDirectory :: Bool
}
deriving (Eq, Show, Data, Typeable)
instance Ord RepoItem where
compare (RepoItem _ _ True) (RepoItem _ _ False) = LT
compare (RepoItem _ _ False) (RepoItem _ _ True) = GT
compare (RepoItem a _ _) (RepoItem b _ _) = compare a b
pathToFile :: [String] -> String
pathToFile [] = ""
pathToFile f = '/' : intercalate "/" f
getFiles :: (P.RepoPatch p, ApplyState p ~ T.Tree)
=> R.Repository p r u t -> [String] -> IO (Maybe [RepoItem])
getFiles r f = do
tree <- repoTree r f
return $ fmap (\t -> sort . map (item t . fst) . onelevel $ t) tree
where
onelevel = filter (\(A.AnchoredPath x, _) -> length x == 1) . T.list
item t a = RepoItem
{ iIsDirectory = isJust (T.findTree t a)
, iPath = "" -- Filled up before sending to template
, iName = fromAnchored a
}
getBlob :: (P.RepoPatch p, ApplyState p ~ T.Tree)
=> R.Repository p r u t -> [String] -> IO (Maybe LBS.ByteString)
getBlob dr@(RI.Repo p _ _ _) f = withCurrentDirectory p $ do
tree <- repoTree dr []
maybe (return Nothing) (\t ->
case T.findFile t (toAnchored f) of
Nothing -> return Nothing
Just b -> fmap Just (T.readBlob b)) tree
isTooLarge :: LBS.ByteString -> Bool
isTooLarge = (> (fromIntegral maxFileDisplaySize)) . LBS.length
repoTree :: (P.RepoPatch p, ApplyState p ~ T.Tree)
=> R.Repository p r u t -> [String] -> IO (Maybe (T.Tree IO))
repoTree r@(RI.Repo p _ _ _) f = do
root <- withCurrentDirectory p (R.readRecorded r >>= T.expand)
return $
if null f
then Just root
else T.findTree root (toAnchored f)
getReadme :: (P.RepoPatch p, ApplyState p ~ T.Tree)
=> R.Repository p r u t -> [String] -> IO (Maybe String)
getReadme dr f = do
tree <- repoTree dr f
case findReadmes tree of
Nothing -> return Nothing
Just r -> do
s <- getBlob dr (f ++ [r])
return $
case s of
Nothing -> Nothing
Just big | isTooLarge big ->
Just "README file too large. Sorry."
Just md | isMarkdown r ->
Just . doMarkdown . fromLBS $ md
Just source ->
Just . fromBS . highlight False r . strictLBS $ source
where
findReadmes
= maybe Nothing listToMaybe
. fmap (filter isReadme . map (fromAnchored . fst) . T.list)
isReadme :: String -> Bool
isReadme f = "readme" `isPrefixOf` f'
where f' = map toLower f
isMarkdown :: String -> Bool
isMarkdown f = or
[ ".markdown" `isSuffixOf` f'
, ".mdwn" `isSuffixOf` f'
, ".md" `isSuffixOf` f'
]
where f' = map toLower f
toAnchored :: [String] -> A.AnchoredPath
toAnchored = A.AnchoredPath . map A.makeName
fromAnchored :: A.AnchoredPath -> String
fromAnchored = fromBS . A.flatten