packages feed

filestore 0.2 → 0.3

raw patch · 7 files changed

+169/−61 lines, 7 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Data.FileStore.Types: type ResourceName = String
+ Data.FileStore.Generic: richDirectory :: FileStore -> FilePath -> IO [(Resource, Either String Revision)]
+ Data.FileStore.Types: FSDirectory :: FilePath -> Resource
+ Data.FileStore.Types: FSFile :: FilePath -> Resource
+ Data.FileStore.Types: data Resource
+ Data.FileStore.Types: directory :: FileStore -> FilePath -> IO [Resource]
+ Data.FileStore.Types: instance Eq Resource
+ Data.FileStore.Types: instance Ord Resource
+ Data.FileStore.Types: instance Read Resource
+ Data.FileStore.Types: instance Show Resource
+ Data.FileStore.Types: instance Typeable Resource
- Data.FileStore.Generic: create :: (Contents a) => FileStore -> ResourceName -> Author -> Description -> a -> IO ()
+ Data.FileStore.Generic: create :: (Contents a) => FileStore -> FilePath -> Author -> Description -> a -> IO ()
- Data.FileStore.Generic: diff :: FileStore -> ResourceName -> Maybe RevisionId -> Maybe RevisionId -> IO [(DI, [String])]
+ Data.FileStore.Generic: diff :: FileStore -> FilePath -> Maybe RevisionId -> Maybe RevisionId -> IO [(DI, [String])]
- Data.FileStore.Generic: modify :: (Contents a) => FileStore -> ResourceName -> RevisionId -> Author -> Description -> a -> IO (Either MergeInfo ())
+ Data.FileStore.Generic: modify :: (Contents a) => FileStore -> FilePath -> RevisionId -> Author -> Description -> a -> IO (Either MergeInfo ())
- Data.FileStore.Generic: searchRevisions :: FileStore -> Bool -> ResourceName -> Description -> IO [Revision]
+ Data.FileStore.Generic: searchRevisions :: FileStore -> Bool -> FilePath -> Description -> IO [Revision]
- Data.FileStore.Generic: smartRetrieve :: (Contents a) => FileStore -> Bool -> ResourceName -> Maybe String -> IO a
+ Data.FileStore.Generic: smartRetrieve :: (Contents a) => FileStore -> Bool -> FilePath -> Maybe String -> IO a
- Data.FileStore.Types: Added :: ResourceName -> Change
+ Data.FileStore.Types: Added :: FilePath -> Change
- Data.FileStore.Types: Deleted :: ResourceName -> Change
+ Data.FileStore.Types: Deleted :: FilePath -> Change
- Data.FileStore.Types: FileStore :: IO () -> (forall a. (Contents a) => ResourceName -> Author -> Description -> a -> IO ()) -> (forall a. (Contents a) => ResourceName -> Maybe RevisionId -> IO a) -> (ResourceName -> Author -> Description -> IO ()) -> (ResourceName -> ResourceName -> Author -> Description -> IO ()) -> ([ResourceName] -> TimeRange -> IO [Revision]) -> (ResourceName -> IO RevisionId) -> (RevisionId -> IO Revision) -> IO [ResourceName] -> (RevisionId -> RevisionId -> Bool) -> (SearchQuery -> IO [SearchMatch]) -> FileStore
+ Data.FileStore.Types: FileStore :: IO () -> (forall a. (Contents a) => FilePath -> Author -> Description -> a -> IO ()) -> (forall a. (Contents a) => FilePath -> Maybe RevisionId -> IO a) -> (FilePath -> Author -> Description -> IO ()) -> (FilePath -> FilePath -> Author -> Description -> IO ()) -> ([FilePath] -> TimeRange -> IO [Revision]) -> (FilePath -> IO RevisionId) -> (RevisionId -> IO Revision) -> IO [FilePath] -> (FilePath -> IO [Resource]) -> (RevisionId -> RevisionId -> Bool) -> (SearchQuery -> IO [SearchMatch]) -> FileStore
- Data.FileStore.Types: Modified :: ResourceName -> Change
+ Data.FileStore.Types: Modified :: FilePath -> Change
- Data.FileStore.Types: SearchMatch :: ResourceName -> Integer -> String -> SearchMatch
+ Data.FileStore.Types: SearchMatch :: FilePath -> Integer -> String -> SearchMatch
- Data.FileStore.Types: delete :: FileStore -> ResourceName -> Author -> Description -> IO ()
+ Data.FileStore.Types: delete :: FileStore -> FilePath -> Author -> Description -> IO ()
- Data.FileStore.Types: history :: FileStore -> [ResourceName] -> TimeRange -> IO [Revision]
+ Data.FileStore.Types: history :: FileStore -> [FilePath] -> TimeRange -> IO [Revision]
- Data.FileStore.Types: index :: FileStore -> IO [ResourceName]
+ Data.FileStore.Types: index :: FileStore -> IO [FilePath]
- Data.FileStore.Types: latest :: FileStore -> ResourceName -> IO RevisionId
+ Data.FileStore.Types: latest :: FileStore -> FilePath -> IO RevisionId
- Data.FileStore.Types: matchResourceName :: SearchMatch -> ResourceName
+ Data.FileStore.Types: matchResourceName :: SearchMatch -> FilePath
- Data.FileStore.Types: rename :: FileStore -> ResourceName -> ResourceName -> Author -> Description -> IO ()
+ Data.FileStore.Types: rename :: FileStore -> FilePath -> FilePath -> Author -> Description -> IO ()
- Data.FileStore.Types: retrieve :: FileStore -> forall a. (Contents a) => ResourceName -> Maybe RevisionId -> IO a
+ Data.FileStore.Types: retrieve :: FileStore -> forall a. (Contents a) => FilePath -> Maybe RevisionId -> IO a
- Data.FileStore.Types: save :: FileStore -> forall a. (Contents a) => ResourceName -> Author -> Description -> a -> IO ()
+ Data.FileStore.Types: save :: FileStore -> forall a. (Contents a) => FilePath -> Author -> Description -> a -> IO ()

Files

CHANGES view
@@ -1,3 +1,27 @@+Version 0.3 released 08 Apr 2009++* Added new 'directory' function, which returns a list of resources given+  a directory name. Resources are marked as either FSFile or+  FSDirectory. Thanks to Thomas Hartman for showing the need for+  'directory', and for distinguishing explicitly between files and+  directories. In 'index' the distinction was previously left implicit,+  which worked for git but not darcs: 'index' provided no way of+  distinguishing an empty directory from a file.++* In 'directory' and 'index', git ls-tree is used instead of git ls-files;+  this guarantees that only files that have been committed are returned.+  'index' now lists only files, and no longer includes empty directories+  even in darcs.++* Added new richDirectory function to Data.FileStore.Generic.+  richDirectory returns a directory that includes information+  about the latest revision of each file. Thanks to Thomas Hartman for+  the patch.++* Replaced ResourceName type with FilePath.++* Added repository information to cabal file.+ Version 0.2 released 08 Feb 2009  * Changed diff to do a line-by-line rather than word-by-word diff.@@ -27,6 +51,8 @@   and for creating a second file in a subdirectory.  * Minor code cleanup.++* Added CHANGES.  Version 0.1 released 24 Jan 2009 
Data/FileStore/Darcs.hs view
@@ -22,12 +22,12 @@ import Data.DateTime (parseDateTime, toSqlString) import Data.FileStore.Types import Data.FileStore.Utils (hashsMatch, isInsideRepo, parseMatchLine, runShellCommand, escapeRegexSpecialChars)-import Data.List (intersect, nub)+import Data.List (intersect, nub, sort, isPrefixOf) import Data.Maybe (fromMaybe, fromJust) import Data.Time.Clock.POSIX (posixSecondsToUTCTime) import System.Directory (doesFileExist, doesDirectoryExist, createDirectoryIfMissing) import System.Exit (ExitCode(ExitSuccess))-import System.FilePath ((</>), takeDirectory, dropFileName)+import System.FilePath ((</>), takeDirectory, dropFileName, addTrailingPathSeparator) import Text.Regex.Posix ((=~)) import Text.XML.Light import qualified Data.ByteString.Lazy as B (ByteString, writeFile)@@ -45,6 +45,7 @@   , latest          = darcsLatestRevId repo   , revision        = darcsGetRevision repo   , index           = darcsIndex repo+  , directory       = darcsDirectory repo   , search          = darcsSearch repo   , idsMatch        = const hashsMatch repo }                    @@ -138,7 +139,7 @@                          return $ lines $ toString r  -- | If name doesn't exist in repo or is not a file, throw NotFound-ensureFileExists :: FilePath -> ResourceName -> IO ()+ensureFileExists :: FilePath -> FilePath -> IO () ensureFileExists repo name = do   isFile <- doesFileExist (repo </> encodeString name)   when (not isFile) $ throwIO NotFound@@ -160,7 +161,7 @@      else throwIO $ UnknownError $ "darcs init failed:\n" ++ err  -- | Save changes (creating the file and directory if needed), add, and commit.-darcsSave :: Contents a => FilePath -> ResourceName -> Author -> Description -> a -> IO ()+darcsSave :: Contents a => FilePath -> FilePath -> Author -> Description -> a -> IO () darcsSave repo name author logMsg contents = do   let filename = repo </> encodeString name   inside <- isInsideRepo repo filename@@ -174,7 +175,7 @@  -- | Commit changes to a resource.  Raise 'Unchanged' exception if there were none. --   This is not for creating a new file; see 'darcsSave'. This is just for updating.-darcsCommit :: FilePath -> [ResourceName] -> Author -> Description -> IO ()+darcsCommit :: FilePath -> [FilePath] -> Author -> Description -> IO () darcsCommit repo names author logMsg = do   let args = ["--all", "-A", (authorName author ++ " <" ++ authorEmail author ++ ">"), "-m", logMsg] ++ names   (statusCommit, errCommit, _) <- runDarcsCommand repo "record" args@@ -185,7 +186,7 @@                        else UnknownError $ "Could not darcs record " ++ unwords names ++ "\n" ++ errCommit  -- | Change the name of a resource.-darcsMove :: FilePath -> ResourceName -> ResourceName -> Author -> Description -> IO ()+darcsMove :: FilePath -> FilePath -> FilePath -> Author -> Description -> IO () darcsMove repo oldName newName author logMsg = do   let newPath = repo </> newName   inside <- isInsideRepo repo newPath@@ -199,7 +200,7 @@      else throwIO NotFound  -- | Delete a resource from the repository.-darcsDelete :: FilePath -> ResourceName -> Author -> Description -> IO ()+darcsDelete :: FilePath -> FilePath -> Author -> Description -> IO () darcsDelete repo name author logMsg = do   runShellCommand repo Nothing "rm" [name]   darcsCommit repo [name] author logMsg@@ -211,7 +212,7 @@  -- | Return list of log entries for the list of resources. -- If list of resources is empty, log entries for all resources are returned.-darcsLog :: FilePath -> [ResourceName] -> TimeRange -> IO [Revision]+darcsLog :: FilePath -> [FilePath] -> TimeRange -> IO [Revision] darcsLog repo names (TimeRange begin end) = do     let opts = timeOpts begin end     do (status, err, output) <- runDarcsCommand repo "changes" $ ["--xml-output", "--summary"] ++ names ++ opts@@ -239,7 +240,7 @@                                 return $ head result  -- | Return revision ID for latest commit for a resource.-darcsLatestRevId :: FilePath -> ResourceName -> IO RevisionId+darcsLatestRevId :: FilePath -> FilePath -> IO RevisionId darcsLatestRevId repo name = do   ensureFileExists repo name   -- changes always succeeds, so no need to check error@@ -252,7 +253,7 @@ -- | Retrieve the contents of a resource. darcsRetrieve :: Contents a             => FilePath-            -> ResourceName+            -> FilePath             -> Maybe RevisionId    -- ^ @Just@ revision ID, or @Nothing@ for latest             -> IO a darcsRetrieve repo name Nothing =@@ -266,14 +267,30 @@      else throwIO $ UnknownError $ "Error in darcs query contents:\n" ++ err  -- | Get a list of all known files inside and managed by a repository.-darcsIndex :: FilePath ->IO [ResourceName]+darcsIndex :: FilePath ->IO [FilePath] darcsIndex repo = do-    (status, errOutput, output) <- runDarcsCommand repo "query"  ["manifest"]-    if status == ExitSuccess-    -- We need to do 'drop 2' because Darcs returns files like ["./foobar"], and-    -- the ./ is always present & always not wanted.-     then return (map (drop 2) . lines . toString $ output)-     else error $ "'darcs query manifest' returned error status.\n" ++ errOutput+  (status, _errOutput, output) <- runDarcsCommand repo "query"  ["files","--no-directories"]+  if status == ExitSuccess+     then return $ map (drop 2) . lines . toString $ output+     else return []   -- return empty list if invalid path (see gitIndex)++-- | Get a list of all resources inside a directory in the repository.+darcsDirectory :: FilePath -> FilePath -> IO [Resource]+darcsDirectory repo dir = do+  let dir' = if null dir then "" else addTrailingPathSeparator dir+  (status1, _errOutput1, output1) <- runDarcsCommand repo "query"  ["files","--no-directories"]+  (status2, _errOutput2, output2) <- runDarcsCommand repo "query" ["files","--no-files"]+  if status1 == ExitSuccess && status2 == ExitSuccess+     then do+       let files = map (drop $ length dir' + 2) . filter (("." </> dir') `isPrefixOf`) . lines . toString $ output1+       -- We need to do 'drop $ length dir' + 3' because Darcs returns files like ["./foo/foobar"].+       let dirs  = map (drop $ length dir' + 2) . filter (("." </> dir') `isPrefixOf`) . drop 1 . lines . toString $ output2+       -- We need the drop 1 to eliminate the root directory, which appears first.+       -- Now, select the ones that are in THIS directory and convert to Resources:+       let files' = map FSFile  $ filter ('/' `notElem`) files+       let dirs'  = map FSDirectory $ filter ('/' `notElem`) dirs+       return $ sort (files' ++ dirs') +     else return []  -- returns empty list for invalid path (see gitDirectory)  -- | Uses grep to search repository. darcsSearch :: FilePath -> SearchQuery -> IO [SearchMatch]
Data/FileStore/Generic.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE ScopedTypeVariables #-} {- |    Module      : Data.FileStore.Generic    Copyright   : Copyright (C) 2009 John MacFarlane, Gwern Branwen, Sebastiaan Visser@@ -17,6 +18,7 @@            , diff            , searchRevisions            , smartRetrieve+           , richDirectory            )  where@@ -26,6 +28,7 @@ import Data.FileStore.Utils import Data.List (isInfixOf) import Data.Algorithm.Diff (DI(..), getGroupedDiff)+import System.FilePath ((</>)) import Prelude hiding (catch)  handleUnknownError :: SomeException -> IO a@@ -35,7 +38,7 @@ -- error. create :: Contents a        => FileStore-       -> ResourceName      -- ^ Resource to create.+       -> FilePath          -- ^ Resource to create.        -> Author            -- ^ Author of change.        -> Description       -- ^ Description of change.        -> a                 -- ^ Contents of resource.@@ -50,7 +53,7 @@ -- @Left@ merge information is returned.  Otherwise, @Right@ the new contents are saved.   modify  :: Contents a         => FileStore-        -> ResourceName      -- ^ Resource to create.+        -> FilePath          -- ^ Resource to create.         -> RevisionId        -- ^ ID of previous revision that is being modified.         -> Author            -- ^ Author of change.         -> Description       -- ^ Description of change.@@ -75,7 +78,7 @@ -- or @B@ (in both), and the list is a list of lines (without -- newlines at the end). diff :: FileStore-     -> ResourceName      -- ^ Resource name to get diff for.+     -> FilePath      -- ^ Resource name to get diff for.      -> Maybe RevisionId  -- ^ @Just@ old revision ID, or @Nothing@ for empty.      -> Maybe RevisionId  -- ^ @Just@ oew revision ID, or @Nothing@ for latest.      -> IO [(DI, [String])]@@ -93,7 +96,7 @@                 -> Bool              -- ^ When true the description must                                      --   match exactly, when false partial                                      --   hits are allowed.-                -> ResourceName      -- ^ The resource to search history for.+                -> FilePath          -- ^ The resource to search history for.                 -> Description       -- ^ Revision description to search for.                 -> IO [Revision] @@ -112,7 +115,7 @@   :: Contents a   => FileStore   -> Bool            -- ^ @True@ for exact description match, @False@ for partial match.-  -> ResourceName    -- ^ Resource name to retrieve.+  -> FilePath        -- ^ Resource name to retrieve.   -> Maybe String    -- ^ @Just@ revision ID or description, or @Nothing@ for empty.   -> IO a smartRetrieve fs exact name mrev = do@@ -135,4 +138,14 @@          -- Retrieve resource for latest matching revision.         else retrieve fs name (Just $ revId $ Prelude.head revs)++-- | Like 'directory', but returns information about the latest revision.+richDirectory :: FileStore -> FilePath -> IO [(Resource, Either String Revision)]+richDirectory fs fp = do+  rs <- directory fs fp+  mapM f rs+  where f r = Control.Exception.catch (g r) (\(e :: FileStoreError)-> return $ ( r, Left . show $ e ) )+        g r@(FSDirectory _dir) = return (r,Left "richDirectory, we don't care about revision info for repos")+        g res@(FSFile file) = do rev <- revision fs =<< latest fs ( fp </> file )+                                 return (res,Right rev) 
Data/FileStore/Git.hs view
@@ -17,6 +17,7 @@            ) where import Data.FileStore.Types+import Data.Maybe (mapMaybe) import System.Exit import Data.Time.Clock.POSIX (posixSecondsToUTCTime) import Data.FileStore.Utils (hashsMatch, isInsideRepo, runShellCommand, escapeRegexSpecialChars) @@ -48,6 +49,7 @@   , latest            = gitLatestRevId repo   , revision          = gitGetRevision repo   , index             = gitIndex repo+  , directory         = gitDirectory repo   , search            = gitSearch repo    , idsMatch          = const hashsMatch repo   }@@ -82,7 +84,7 @@  -- | Commit changes to a resource.  Raise 'Unchanged' exception if there were -- no changes.-gitCommit :: FilePath -> [ResourceName] -> Author -> String -> IO ()+gitCommit :: FilePath -> [FilePath] -> Author -> String -> IO () gitCommit repo names author logMsg = do   (statusCommit, errCommit, _) <- runGitCommand repo "commit" $ ["--author", authorName author ++ " <" ++                                     authorEmail author ++ ">", "-m", logMsg] ++ names@@ -93,7 +95,7 @@                        else UnknownError $ "Could not git commit " ++ unwords names ++ "\n" ++ errCommit  -- | Save changes (creating file and directory if needed), add, and commit.-gitSave :: Contents a => FilePath -> ResourceName -> Author -> Description -> a -> IO ()+gitSave :: Contents a => FilePath -> FilePath -> Author -> Description -> a -> IO () gitSave repo name author logMsg contents = do   let filename = repo </> encodeString name   inside <- isInsideRepo repo filename@@ -108,7 +110,7 @@ -- | Retrieve contents from resource. gitRetrieve :: Contents a             => FilePath-            -> ResourceName+            -> FilePath             -> Maybe RevisionId    -- ^ @Just@ revision ID, or @Nothing@ for latest             -> IO a gitRetrieve repo name revid = do@@ -124,7 +126,7 @@      else throwIO $ UnknownError $ "Error in git cat-file:\n" ++ err'  -- | Delete a resource from the repository.-gitDelete :: FilePath -> ResourceName -> Author -> Description -> IO ()+gitDelete :: FilePath -> FilePath -> Author -> Description -> IO () gitDelete repo name author logMsg = do   (statusAdd, errRm, _) <- runGitCommand repo "rm" [name]   if statusAdd == ExitSuccess@@ -132,7 +134,7 @@      else throwIO $ UnknownError $ "Could not git rm '" ++ name ++ "'\n" ++ errRm  -- | Change the name of a resource.-gitMove :: FilePath -> ResourceName -> ResourceName -> Author -> Description -> IO ()+gitMove :: FilePath -> FilePath -> FilePath -> Author -> Description -> IO () gitMove repo oldName newName author logMsg = do   gitLatestRevId repo oldName   -- will throw a NotFound error if oldName doesn't exist   let newPath = repo </> encodeString newName@@ -146,7 +148,7 @@      else throwIO $ UnknownError $ "Could not git mv " ++ oldName ++ " " ++ newName ++ "\n" ++ err  -- | Return revision ID for latest commit for a resource.-gitLatestRevId :: FilePath -> ResourceName -> IO RevisionId+gitLatestRevId :: FilePath -> FilePath -> IO RevisionId gitLatestRevId repo name = do   (status, _, output) <- runGitCommand repo "rev-list" ["--max-count=1", "HEAD", "--", name]   if status == ExitSuccess@@ -169,14 +171,29 @@                  Right xs    -> throwIO $ UnknownError $ "git rev-list returned more than one result: " ++ show xs      else throwIO NotFound --- | Get list of files in repository.-gitIndex :: FilePath -> IO [ResourceName]+-- | Get a list of all known files inside and managed by a repository.+gitIndex :: FilePath ->IO [FilePath] gitIndex repo = do-  (status, errOutput, output) <- runGitCommand repo "ls-files" []+  (status, _err, output) <- runGitCommand repo "ls-tree" ["-r","-t","HEAD"]   if status == ExitSuccess-     then return $ map convertEncoded $ lines $ toString output-     else error $ "git ls-files returned error status.\n" ++ errOutput+     then return $ mapMaybe (lineToFilename . words) . lines . toString $ output+     else return [] -- if error, will return empty list+                    -- note:  on a newly initialized repo, 'git ls-tree HEAD' returns an error+   where lineToFilename (_:"blob":_:rest) = Just $ convertEncoded $ unwords rest+         lineToFilename _                 = Nothing +-- | Get list of resources in one directory of the repository.+gitDirectory :: FilePath -> FilePath -> IO [Resource]+gitDirectory repo dir = do+  (status, _err, output) <- runGitCommand repo "ls-tree" ["HEAD:" ++ dir]+  if status == ExitSuccess+     then return $ map (lineToResource . words) $ lines $ toString output+     else return []   -- if error, this will return empty list+                      -- note:  on a newly initialized repo, 'git ls-tree HEAD:' returns an error+   where lineToResource (_:"blob":_:rest) = FSFile $ convertEncoded $ unwords rest+         lineToResource (_:"tree":_:rest) = FSDirectory $ convertEncoded $ unwords rest+         lineToResource _                 = error "Encountered an item that is neither blob nor tree in git ls-tree"+ -- | Uses git-grep to search repository.  Escape regex special characters, so the pattern -- is interpreted as an ordinary string. gitSearch :: FilePath -> SearchQuery -> IO [SearchMatch]@@ -199,7 +216,7 @@  {- -- | Uses git-diff to get a dif between two revisions.-gitDiff :: FilePath -> ResourceName -> RevisionId -> RevisionId -> IO String+gitDiff :: FilePath -> FilePath -> RevisionId -> RevisionId -> IO String gitDiff repo name from to = do   (status, _, output) <- runGitCommand repo "diff" [from, to, name]   if status == ExitSuccess@@ -214,7 +231,7 @@  -- | Return list of log entries for the given time frame and list of resources. -- If list of resources is empty, log entries for all resources are returned.-gitLog :: FilePath -> [ResourceName] -> TimeRange -> IO [Revision]+gitLog :: FilePath -> [FilePath] -> TimeRange -> IO [Revision] gitLog repo names (TimeRange mbSince mbUntil) = do   (status, err, output) <- runGitCommand repo "whatchanged" $                            ["--pretty=format:%h%n%ct%n%an%n%ae%n%s%n"] ++
Data/FileStore/Types.hs view
@@ -13,7 +13,7 @@  module Data.FileStore.Types            ( RevisionId-           , ResourceName+           , Resource(..)            , Author(..)            , Change(..)            , Description@@ -35,10 +35,13 @@ import Data.DateTime (DateTime) import Control.Exception (Exception) import Prelude hiding (catch)+import System.FilePath (FilePath)  type RevisionId   = String -type ResourceName = String+data Resource = FSFile FilePath+              | FSDirectory FilePath+              deriving (Show, Read, Eq, Typeable, Ord)  data Author =   Author {@@ -47,9 +50,9 @@   } deriving (Show, Read, Eq, Typeable)  data Change =-    Added ResourceName-  | Deleted ResourceName-  | Modified ResourceName+    Added FilePath+  | Deleted FilePath+  | Modified FilePath   deriving (Show, Read, Eq, Typeable)  type Description = String@@ -127,7 +130,7 @@  data SearchMatch =   SearchMatch {-    matchResourceName :: ResourceName+    matchResourceName :: FilePath   , matchLineNumber   :: Integer   , matchLine         :: String   } deriving (Show, Read, Eq, Typeable)@@ -141,7 +144,7 @@      -- | Save contents in the filestore.   , save           :: Contents a-                   => ResourceName      -- Resource to save.+                   => FilePath          -- Resource to save.                    -> Author            --  Author of change.                    -> Description       --  Description of change.                    -> a                 --  New contents of resource.@@ -149,32 +152,32 @@          -- | Retrieve the contents of the named resource.   , retrieve       :: Contents a-                   => ResourceName      -- Resource to retrieve.+                   => FilePath          -- Resource to retrieve.                    -> Maybe RevisionId  -- @Just@ a particular revision ID, or @Nothing@ for latest                    -> IO a      -- | Delete a named resource, providing author and log message.-  , delete         :: ResourceName      -- Resource to delete.+  , delete         :: FilePath          -- Resource to delete.                    -> Author            -- Author of change.                    -> Description       -- Description of change.                    -> IO ()      -- | Rename a resource, providing author and log message.-  , rename         :: ResourceName      -- Resource original name.-                   -> ResourceName      -- Resource new name.+  , rename         :: FilePath          -- Resource original name.+                   -> FilePath          -- Resource new name.                    -> Author            -- Author of change.                    -> Description       -- Description of change.                    -> IO ()      -- | Get history for a list of named resources in a (possibly openended) time range.     -- If the list is empty, history for all resources will be returned. -  , history        :: [ResourceName]    -- List of resources to get history for, or @[]@ for all.+  , history        :: [FilePath]        -- List of resources to get history for, or @[]@ for all.                    -> TimeRange         -- Time range within which to get history.                    -> IO [Revision]      -- | Return the revision ID of the latest change for a resource.  Raises 'NotFound'     -- if the resource is not found.-  , latest         :: ResourceName      -- Resource to get revision ID for.+  , latest         :: FilePath          -- Resource to get revision ID for.                    -> IO RevisionId      -- | Return information about a revision, given the ID.  Raises 'NotFound' if there is@@ -183,7 +186,11 @@                    -> IO Revision      -- | Return a list of resources in the filestore.-  , index          :: IO [ResourceName]+  , index          :: IO [FilePath]++  -- | Return a list of resources in a directory of the filestore.+  , directory      :: FilePath          -- Directory to list (empty for root)+                   -> IO [Resource]      -- | @True@ if the revision IDs match, in the sense that the     -- can be treated as specifying the same revision.
Tests.lhs view
@@ -6,6 +6,7 @@     runghc -idist/build/autogen Tests.lhs  > import Data.FileStore+> import Data.List (sort) > import Test.HUnit > import System.Directory (removeDirectoryRecursive) > import Control.Monad (forM)@@ -14,6 +15,7 @@ > import Data.DateTime > import Data.Maybe (mapMaybe) > import System.Directory (doesFileExist)+> import System.FilePath > import System.Process > import Data.Algorithm.Diff (DI(..)) @@ -33,6 +35,7 @@ >     , ("create resource in subdirectory", createTest2) >     , ("create resource with non-ascii name", createTest3) >     , ("try to create resource outside repo", createTest4)+>     , ("directory", directoryTest) >     , ("retrieve resource", retrieveTest1) >     , ("retrieve resource in a subdirectory", retrieveTest2) >     , ("retrieve resource with non-ascii name", retrieveTest3)@@ -84,7 +87,7 @@ >   create fs subdirTestTitle testAuthor "description of change" testContents >   revid <- latest fs testTitle >   assertBool "revision returns a revision after create" (not (null revid))->   create fs "subdir/Subdir title2.txt" testAuthor "+Second file" testContents+>   create fs (subdirTestTitle ++ "2") testAuthor "+Second file" testContents  *** Create a resource with a non-ascii title, and check to see that revision returns a revision for it: @@ -102,6 +105,20 @@ >   exists <- doesFileExist "tmp/oops" >   assertBool "file ../oops was created outside repository" (not exists) +*** Test directory++> directoryTest fs = TestCase $ do++    Get directory for top-level++>   files <- directory fs ""+>   assertEqual "result of directory on top-level" (sort [FSDirectory "subdir", FSFile testTitle, FSFile nonasciiTestTitle]) (sort files)++    Get contents of subdirectory++>   subdirFiles <- directory fs "subdir"+>   assertEqual "result of directory on subdir" [FSFile (takeFileName subdirTestTitle), FSFile (takeFileName (subdirTestTitle ++ "2"))] subdirFiles+ *** Retrieve latest version of a resource:  > retrieveTest1 fs = TestCase $ do@@ -180,7 +197,7 @@  >   delete fs toBeDeleted testAuthor "goodbye" >   ind <- index fs->   assertBool "index does not contain resource that was deleted" (not (toBeDeleted `elem` ind))+>   assertBool "index does not contain resource that was deleted" (toBeDeleted `notElem` ind)  *** Rename a resource: 
filestore.cabal view
@@ -1,5 +1,5 @@ Name:                filestore-Version:             0.2+Version:             0.3 Cabal-version:       >= 1.2 Build-type:          Custom Tested-with:         GHC==6.10.1@@ -14,13 +14,24 @@ License-File:        LICENSE Author:              John MacFarlane, Gwern Branwen, Sebastiaan Visser Maintainer:          jgm@berkeley.edu-Build-depends:       base >= 4, bytestring, utf8-string, filepath, directory, datetime,-                     parsec >= 2 && < 3, process, time, datetime, regex-posix, xml, split, Diff-Extra-source-files:  Tests.lhs-Exposed-modules:     Data.FileStore, Data.FileStore.Types, Data.FileStore.Git, Data.FileStore.Darcs,-                     -- Data.FileStore.Sqlite3,-                     Data.FileStore.Utils, Data.FileStore.Generic-Other-modules:       Paths_filestore+Homepage:            http://johnmacfarlane.net/repos/filestore+ Data-Files:          extra/post-update, CHANGES-Ghc-Options:         -Wall-Ghc-Prof-Options:    -auto-all+Extra-source-files:  Tests.lhs++-- Commented out until Cabal 1.6 becomes common.+-- Source-repository head+--    Type:            darcs+--    Location:        http://johnmacfarlane.net/repos/filestore++Library+    Build-depends:       base >= 4, bytestring, utf8-string, filepath, directory, datetime,+                         parsec >= 2 && < 3, process, time, datetime, regex-posix, xml, split, Diff++    Exposed-modules:     Data.FileStore, Data.FileStore.Types, Data.FileStore.Git, Data.FileStore.Darcs,+                         -- Data.FileStore.Sqlite3,+                         Data.FileStore.Utils, Data.FileStore.Generic+    Other-modules:       Paths_filestore++    Ghc-Options:         -Wall+    Ghc-Prof-Options:    -auto-all