packages feed

dirstream (empty) → 1.0.0

raw patch · 4 files changed

+241/−0 lines, 4 filesdep +Win32dep +basedep +directorysetup-changed

Dependencies added: Win32, base, directory, pipes, pipes-safe, system-fileio, system-filepath, unix

Files

+ LICENSE view
@@ -0,0 +1,24 @@+Copyright (c) 2013 Gabriel Gonzalez+All rights reserved.++Redistribution and use in source and binary forms, with or without modification,+are permitted provided that the following conditions are met:+    * Redistributions of source code must retain the above copyright notice,+      this list of conditions and the following disclaimer.+    * Redistributions in binary form must reproduce the above copyright notice,+      this list of conditions and the following disclaimer in the documentation+      and/or other materials provided with the distribution.+    * Neither the name of Gabriel Gonzalez nor the names of other contributors+      may be used to endorse or promote products derived from this software+      without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ dirstream.cabal view
@@ -0,0 +1,42 @@+Name: dirstream+Version: 1.0.0+Cabal-Version: >=1.8.0.2+Build-Type: Simple+License: BSD3+License-File: LICENSE+Copyright: 2013 Gabriel Gonzalez+Author: Gabriel Gonzalez+Maintainer: Gabriel439@gmail.com+Bug-Reports: https://github.com/Gabriel439/Haskell-Dirstream-Library/issues+Synopsis: Easily stream directory contents in constant memory+Description: Use this library to read large directories as streams:+  .+  * Use @ListT@ to assemble recursive directory traversals while still streaming+  .+  * Use @pipes@ to read out the stream of results+  .+  * Traversals only open directory streams lazily in response to demand+  .+  * Avoid directories with insufficient permissions+  .+  This library works on both Unix, OS X, and Windows.+Category: System, Pipes+Source-Repository head+    Type: git+    Location: https://github.com/Gabriel439/Haskell-Dirstream-Library++Library+    Hs-Source-Dirs: src+    Build-Depends:+        base            >= 4       && < 5  ,+        directory                     < 1.3,+        system-filepath >= 0.3.1   && < 0.5,+        system-fileio   >= 0.2.1   && < 0.4,+        pipes           >= 4.0     && < 4.2,+        pipes-safe      >= 2.0.0   && < 2.3+    if os(windows)+        Build-Depends: Win32 >= 2.2.0.1 && < 2.4+    else+        Build-Depends: unix  >= 2.5.1.0 && < 2.8+    Exposed-Modules: Data.DirStream+    GHC-Options: -O2 -Wall
+ src/Data/DirStream.hs view
@@ -0,0 +1,173 @@+{-# LANGUAGE CPP, OverloadedStrings, FlexibleContexts #-}++{-| Use this module to stream directory contents lazily in constant memory in+    conjunction with @pipes@+-}++module Data.DirStream+    ( -- * Directory Traversals+      -- $traversals+      childOf+    , descendentOf++    -- * Utilities+    , unixVisible+    , isDirectory++    -- * Tutorial+    -- $tutorial+    ) where++import Control.Applicative ((<|>))+import Control.Monad (when)+#ifdef mingw32_HOST_OS+import Data.Bits ((.&.))+#endif+import Data.List (isPrefixOf)+import Pipes (ListT(Select), yield, liftIO, MonadIO)+import Pipes.Safe (bracket, MonadSafe, Base)+import System.Directory (readable, getPermissions)+import qualified Filesystem.Path.CurrentOS as F+import Filesystem.Path ((</>))+import Filesystem (isDirectory)+#ifdef mingw32_HOST_OS+import qualified System.Win32 as Win32+#else+import System.Posix (openDirStream, readDirStream, closeDirStream)+#endif++{- $traversals+    There many possible recursion schemes for traversing directories.  Rather+    than provide them all, I prefer that you learn to assemble your own+    recursion schemes, using the source code for 'descendentOf' as a starting+    point.+-}++#ifdef mingw32_HOST_OS+fILE_ATTRIBUTE_REPARSE_POINT :: Win32.FileAttributeOrFlag+fILE_ATTRIBUTE_REPARSE_POINT = 1024++reparsePoint :: Win32.FileAttributeOrFlag -> Bool+reparsePoint attr = fILE_ATTRIBUTE_REPARSE_POINT .&. attr /= 0+#endif++{-| Select all immediate children of the given directory, ignoring @\".\"@ and+    @\"..\"@.++    Returns zero children if the directory is not readable or (on Windows) if+    the directory is actually a reparse point.+-}+childOf :: MonadSafe m => F.FilePath -> ListT m F.FilePath+childOf path = Select $ do+    let path' = F.encodeString path+    canRead <- liftIO $ fmap readable $ getPermissions path'+#ifdef mingw32_HOST_OS+    reparse <- liftIO $ fmap reparsePoint $ Win32.getFileAttributes path'+    when (canRead && not reparse) $+        bracket+            (liftIO $ Win32.findFirstFile (F.encodeString (path </> "*")))+            (\(h, _) -> liftIO $ Win32.findClose h)+            $ \(h, fdat) -> do+                let loop = do+                        file' <- liftIO $ Win32.getFindDataFileName fdat+                        let file = F.decodeString file'+                        when (file' /= "." && file' /= "..") $+                            yield (path </> file)+                        more  <- liftIO $ Win32.findNextFile h fdat+                        when more loop+                loop+#else+    when (canRead) $+        bracket (liftIO $ openDirStream path') (liftIO . closeDirStream) $+            \dirp -> do+            let loop = do+                    file' <- liftIO $ readDirStream dirp+                    case file' of+                        [] -> return ()+                        _  -> do+                            let file = F.decodeString file'+                            when (file' /= "." && file' /= "..") $+                                yield (path </> file)+                            loop+            loop+#endif+{-# INLINABLE childOf #-}++-- | Select all recursive descendents of the given directory+descendentOf :: MonadSafe m => F.FilePath -> ListT m F.FilePath+descendentOf path = do+    child <- childOf path+    isDir <- liftIO $ isDirectory child+    if isDir+        then return child <|> descendentOf child+        else return child+{-# INLINABLE descendentOf #-}++{-| Determine if a file is visible according to Unix conventions, defined as the+    base name not beginning with a @\'.\'@+-}+unixVisible :: F.FilePath -> Bool+unixVisible path = not $ "." `isPrefixOf` F.encodeString (F.basename path)+{-# INLINABLE unixVisible #-}++-- $tutorial+--  The following example shows a simple program that enumerates the contents of+--  a single directory:+-- +-- > {-# LANGUAGE OverloadedStrings #-}+-- >+-- > import Data.DirStream+-- > import Pipes+-- > import Pipes.Safe+-- >+-- > main1 = runSafeT $ runEffect $+-- >     for (every (childOf "/tmp")) (liftIO . print)+--+-- >>> main1+-- FilePath "/tmp"+-- FilePath "/tmp/dir1"+-- FilePath "/tmp/dir2"+-- FilePath "/tmp/fileE"+--+--  The 'childOf' function streams the list of files in constant memory,+--  allowing you to traverse very large directory lists.+--+--  You can use 'ListT' to assemble more sophisticated traversals, such as the+--  recursive 'descendentOf' traversal, which has the following definition:+--+-- > descendentOf :: F.FilePath -> ListT (SafeT IO) F.FilePath+-- > descendentOf path = do+-- >     child <- childOf path+-- >     isDir <- liftIO $ isDirectory child+-- >     if isDir+-- >         then return child <|> descendentOf child+-- >         else return child+--+--  These recursive traversals will promptly open and close nested directory+--  streams as they traverse the directory tree:+--+-- > main2 = runSafeT $ runEffect $+-- >     for (every (descendentOf "/tmp")) (liftIO . print)+--+-- >>> main2+-- FilePath "/tmp"+-- FilePath "/tmp/dir1"+-- FilePath "/tmp/dir1/fileA"+-- FilePath "/tmp/dir1/fileB"+-- FilePath "/tmp/dir2"+-- FilePath "/tmp/dir2/fileC"+-- FilePath "/tmp/dir2/fileD"+-- FilePath "/tmp/fileE"+--+--  These traverals are lazy and will open the minimal number of directories+--  necessary to satisfy downstream demand:+--+-- > import qualified Pipes.Prelude as P+-- >+-- > main3 = runSafeT $ runEffect $+-- >     for (every (descendentOf "/tmp") >-> P.take 3) (liftIO . print)+--+-- >>> main3  -- This never opens the "/tmp/dir2" directory+-- FilePath "/tmp"+-- FilePath "/tmp/dir1"+-- FilePath "/tmp/dir1/fileA"