find-conduit 0.2.2 → 0.2.3
raw patch · 3 files changed
+199/−37 lines, 3 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Data.Conduit.Find: (=~) :: FilePath -> Text -> Bool
Files
- Data/Conduit/Find.hs +179/−25
- Data/Conduit/Find/Looped.hs +17/−10
- find-conduit.cabal +3/−2
Data/Conduit/Find.hs view
@@ -1,43 +1,65 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ViewPatterns #-} module Data.Conduit.Find- ( FileEntry(..)- , Predicate- , HasFileInfo(..)- , consider- , entryPath- , matchAll- , ignoreAll+ (+ -- * Introduction+ -- $intro++ -- ** Basic comparison with GNU find+ -- $gnufind++ -- ** Performance+ -- $performance++ -- ** Other notes+ -- $notes++ -- * Finding functions+ find+ , find'+ , lfind+ , lfind'+ , findWithPreFilter+ , readPaths+ , stat+ , lstat+ , test++ -- * File path predicates , ignoreVcs , regex , glob- , stat- , lstat- , regular- , hasMode- , executable , filename_ , filenameS_ , filepath_ , filepathS_+ , withPath+ , entryPath++ -- * File entry predicates (uses stat information)+ , regular+ , hasMode+ , executable , depth , lastAccessed , lastModified- , withPath , withStatus- , prune- , test- , find- , find'- , lfind- , lfind'- , findWithPreFilter- , readPaths++ -- * Predicate combinators , or_ , and_ , not_+ , prune+ , matchAll+ , ignoreAll+ , consider+ , (=~)++ -- * Types and type classes+ , FileEntry(..)+ , Predicate+ , HasFileInfo(..) ) where import Conduit@@ -55,8 +77,126 @@ import Prelude hiding (FilePath) import System.Posix.Files import System.Posix.Types-import Text.Regex.Posix ((=~))+import qualified Text.Regex.Posix as R ((=~)) +{- $intro++**find-conduit** is essentially a souped version of GNU find for Haskell,+using a DSL to provide both ease of us, and extensive flexbility.++In its simplest form, let's compare some uses of find to find-conduit. Bear+in mind that the result of the find function is a conduit, so you're expected+to either sink it to a list, or operate on the file paths as they are yielded.+-}++{- $gnufind++A typical find command:++@+find src -name '*.hs' -type f -print+@++Would in find-conduit be:++@+find "src" (glob "*.hs" \<\> regular) $$ mapM_C (liftIO . print)+@++The 'glob' predicate matches the file basename against the globbing pattern,+while the 'regular' predicate matches plain files.++A more complicated example:++@+find . -size +100M -perm 644 -mtime 1+@++Now in find-conduit:++@+let megs = 1024 * 1024+ days = 86400+now <- liftIO getCurrentTime+find \".\" ( fileSize (> 100*megs)+ \<\> hasMode 0o644+ \<\> lastModified (> addUTCTime now (-(1*days)))+ )+@++Appending predicates like this expressing an "and" relationship. Use '<|>' to+express "or". You can also negate any predicate:++@+find \".\" (not_ (hasMode 0o644))+@++By default, predicates, whether matching or not, will allow recursion into+directories. In order to express that matching predicate should disallow+recursion, use 'prune':++@+find \".\" (prune (depth (> 2)))+@++This is the same as using '-maxdepth 2' in find.++@+find \".\" (prune (filename_ (== "dist")))+@++This is the same as:++@+find . \\( -name dist -prune \\) -o -print+@+-}++{- $performance++find-conduit strives to make file-finding a well performing operation. To+this end, a composed Predicate will only call stat once per entry being+considered; and if you prune a directory, it is not traversed at all.++By default, 'find' calls stat for every file before it applies the predicate,+in order to ensure that only one such call is needed. Sometimes, however, you+know just from the FilePath that you don't want to consider a certain file, or+you want to prune a directory tree.++To support these types of optimized queries, a variant of find is provided+called 'findWithPreFilter'. This takes two predicates: one that is applied to+only the FilePath, before stat (or lstat) is called; and one that is applied+to the full file information after the stat.+-}++{- $notes++Predicates form a Category and an Arrow, so you can use Arrow-style+composition rather than Monoids if you wish. They also form an Applicative, a+Monad and a MonadPlus.++In the Monad, the value bound over is whatever the predicate chooses to return+(most Predicates return the same FilePath they examined, however, making the+Monad less value). Here's an example Monad: If the find takes longer than 5+minutes, abort. We could have used 'timeout', but this is for illustration.++@+start <- liftIO getCurrentTime+find \".\" $ do+ glob \"*.hs\"++ end <- liftIO getCurrentTime+ if diffUTCTIme end start > 300+ then ignoreAll+ else matchAll+@++The Predicate Monad is a short-circuiting monad, meaning we stop as soon as it+can be determined that the user is not interested in a given file. To access+the current file, simply bind the result value from any Predicate. To change+the file being matched against,for whatever reason, use 'consider'.+-}+ data FileInfo = FileInfo { infoPath :: FilePath , infoDepth :: Int@@ -98,9 +238,23 @@ where vcsDirs = [ ".git", "CVS", "RCS", "SCCS", ".svn", ".hg", "_darcs" ] --- | Find every entry whose filename part matching the given regular expression. regex :: (Monad m, HasFileInfo e) => Text -> Predicate m e-regex pat = filenameS_ (=~ unpack pat)+regex pat = filename_ (=~ pat)++-- | This is a re-export of 'Text.Regex.Posix.=~', with the types changed for+-- ease of use with this module. For example, you can simply say:+--+-- @+-- filename_ (=~ "\\.hs$")+-- @+--+-- Which is the same thing as:+--+-- @+-- regex "\\.hs$"+-- @+(=~) :: FilePath -> Text -> Bool+str =~ pat = encodeString str R.=~ unpack pat -- | Find every entry whose filename part matching the given filename globbing -- expression. For example: @glob "*.hs"@.
Data/Conduit/Find/Looped.hs view
@@ -103,7 +103,7 @@ KeepAndRecurse b _ -> runLooped (k b) a instance Monad m => Category (Looped m) where- id = let x = Looped $ \a -> return $ KeepAndRecurse a x in x+ id = matchAll Looped f . Looped g = Looped $ \a -> do r <- g a case r of@@ -139,15 +139,15 @@ -- | Within a predicate block, 'consider' a different item than what is -- currently being predicated upon. This makes it possible to write custom--- logic within the Monad instance for Looped, such as in this contrived--- example:+-- logic within the Monad instance for a predicate, such as in this+-- contrived example: -- -- @--- flip runLooped "bar.hs" $ do--- x <- filename_ (== "foo.hs")--- when (x /= "") $--- consider "baz.hs" $--- filename_ (== "baz.hs") -- passes+-- flip runLooped \"bar.hs\" $ do+-- x <- filename_ (== \"foo.hs\")+-- when (x /= \"\") $+-- consider \"baz.hs\" $+-- filename_ (== \"baz.hs\") -- @ consider :: a -> Looped m a b -> Looped m a b consider x l = Looped $ const $ runLooped l x@@ -242,7 +242,7 @@ f `mappend` g = f <> g instance Monad m => MonadPlus (Looped m a) where- mzero = Looped $ const $ return Ignore+ mzero = ignoreAll Looped f `mplus` Looped g = Looped $ \a -> do r <- f a case r of@@ -251,11 +251,18 @@ RecurseOnly _ -> g a KeepAndRecurse b m -> return $ KeepAndRecurse b m +-- | 'matchAll' is id in the 'Predicate' Category. matchAll :: Monad m => Predicate m a matchAll = Looped $ \entry -> return $ KeepAndRecurse entry matchAll +-- | 'ignore' rejects the current entry, but allows recursion.+ignore :: Monad m => Looped m a b+ignore = Looped $ const $ return $ RecurseOnly ignore++-- | 'ignoreAll' rejects every entry, and does not recurse. This is the same+-- as 'mzero'. ignoreAll :: Monad m => Looped m a b-ignoreAll = Looped $ const $ return $ RecurseOnly ignoreAll+ignoreAll = Looped $ const $ return Ignore -- | 'not_' reverse the meaning of the given predicate, preserving recursion. not_ :: MonadIO m => Predicate m a -> Predicate m a
find-conduit.cabal view
@@ -1,5 +1,5 @@ Name: find-conduit-Version: 0.2.2+Version: 0.2.3 Synopsis: A file-finding conduit that allows user control over traversals. License-file: LICENSE License: MIT@@ -9,7 +9,8 @@ Cabal-Version: >=1.10 Category: System Description:- A file-finding conduit that allows user control over traversals.+ A file-finding conduit that allows user control over traversals. Please see+ the module 'Data.Conduit.Find' for more information. Source-repository head type: git