System.Directory.Tree

directory-tree-0.11.0: A simple directory-like tree datatype, with useful IO functions

Copyright | (c) Brandon Simmons
License | BSD3
Maintainer | Brandon Simmons <brandon.m.simmons@gmail.com>
Stability | experimental
Portability | portable Provides a simple data structure mirroring a directory tree on the
Safe Haskell | None
Language | Haskell98

System.Directory.Tree

Contents

- Data types for representing directory trees
- High level IO functions
- Lower level functions
  - Manipulating FilePaths
- Utility functions
  - Shape comparison and equality
  - Handling failure
  - Tree Manipulations
  - Navigation
  - Operators
- Lenses

Description

filesystem, as well as useful functions for reading and writing file and directory structures in the IO monad.

Errors are caught in a special constructor in the DirTree type.

Defined instances of Functor, Traversable and Foldable allow for easily operating on a directory of files. For example, you could use Foldable.foldr to create a hash of the entire contents of a directory.

The functions `readDirectoryWithL` and `buildL` allow for doing directory-traversing IO lazily as required by the execution of pure code. This allows you to treat large directories the same way as you would a lazy infinite list.

The AnchoredDirTree type is a simple wrapper for DirTree to keep track of a base directory context for the DirTree.

Please send me any requests, bugs, or other feedback on this module!

Synopsis

- data DirTree a
  - = Failed {
    - name :: FileName
    - err :: IOException
    }
  - | Dir {
    - name :: FileName
    - contents :: [DirTree a]
    }
  - | File {
    - name :: FileName
    - file :: a
    }
- data AnchoredDirTree a = (:/) {
  - anchor :: FilePath
  - dirTree :: DirTree a
  }
- type FileName = String
- readDirectory :: FilePath -> IO (AnchoredDirTree String)
- readDirectoryWith :: (FilePath -> IO a) -> FilePath -> IO (AnchoredDirTree a)
- readDirectoryWithL :: (FilePath -> IO a) -> FilePath -> IO (AnchoredDirTree a)
- writeDirectory :: AnchoredDirTree String -> IO (AnchoredDirTree ())
- writeDirectoryWith :: (FilePath -> a -> IO b) -> AnchoredDirTree a -> IO (AnchoredDirTree b)
- build :: FilePath -> IO (AnchoredDirTree FilePath)
- buildL :: FilePath -> IO (AnchoredDirTree FilePath)
- openDirectory :: FilePath -> IOMode -> IO (AnchoredDirTree Handle)
- writeJustDirs :: AnchoredDirTree a -> IO (AnchoredDirTree a)
- zipPaths :: AnchoredDirTree a -> DirTree (FilePath, a)
- free :: AnchoredDirTree a -> DirTree a
- equalShape :: DirTree a -> DirTree b -> Bool
- comparingShape :: DirTree a -> DirTree b -> Ordering
- successful :: DirTree a -> Bool
- anyFailed :: DirTree a -> Bool
- failed :: DirTree a -> Bool
- failures :: DirTree a -> [DirTree a]
- failedMap :: (FileName -> IOException -> DirTree a) -> DirTree a -> DirTree a
- flattenDir :: DirTree a -> [DirTree a]
- sortDir :: Ord a => DirTree a -> DirTree a
- sortDirShape :: DirTree a -> DirTree a
- filterDir :: (DirTree a -> Bool) -> DirTree a -> DirTree a
- dropTo :: FileName -> AnchoredDirTree a -> Maybe (AnchoredDirTree a)
- (</$>) :: Functor f => (DirTree a -> DirTree b) -> f (AnchoredDirTree a) -> f (AnchoredDirTree b)
- _contents :: Applicative f => ([DirTree a] -> f [DirTree a]) -> DirTree a -> f (DirTree a)
- _err :: Applicative f => (IOException -> f IOException) -> DirTree a -> f (DirTree a)
- _file :: Applicative f => (a -> f a) -> DirTree a -> f (DirTree a)
- _name :: Functor f => (FileName -> f FileName) -> DirTree a -> f (DirTree a)
- _anchor :: Functor f => (FilePath -> f FilePath) -> AnchoredDirTree a -> f (AnchoredDirTree a)
- _dirTree :: Functor f => (DirTree t -> f (DirTree a)) -> AnchoredDirTree t -> f (AnchoredDirTree a)

# Data types for representing directory trees

data DirTree a

the String in the name field is always a file name, never a full path. The free type variable is used in the File constructor and can hold Handles, Strings representing a file's contents or anything else you can think of. We catch any IO errors in the Failed constructor. an Exception can be converted to a String with `show`.

Constructors

Failed |
Fields name :: FileName err :: IOException
Dir |
Fields name :: FileName contents :: [DirTree a]
File |
Fields name :: FileName file :: a

Instances

Functor DirTree |
Foldable DirTree |
Traversable DirTree |
Eq a => Eq (DirTree a) | Two DirTrees are equal if they have the same constructor, the same name (and in the case of `Dir`s) their sorted `contents` are equal:
(Ord a, Eq a) => Ord (DirTree a) | First compare constructors: Failed < Dir < File... Then compare `name`... Then compare free variable parameter of `File` constructors
Show a => Show (DirTree a) |

data AnchoredDirTree a

a simple wrapper to hold a base directory name, which can be either an absolute or relative path. This lets us give the DirTree a context, while still letting us store only directory and file names (not full paths) in the DirTree. (uses an infix constructor; don't be scared)

Constructors

(:/) |
Fields anchor :: FilePath dirTree :: DirTree a

Instances

Functor AnchoredDirTree |
Eq a => Eq (AnchoredDirTree a) |
Ord a => Ord (AnchoredDirTree a) |
Show a => Show (AnchoredDirTree a) |

type FileName = String

an element in a FilePath:

# High level IO functions

readDirectory :: FilePath -> IO (AnchoredDirTree String)

build an AnchoredDirTree, given the path to a directory, opening the files using readFile. Uses `readDirectoryWith` internally and has the effect of traversing the entire directory structure. See `readDirectoryWithL` for lazy production of a DirTree structure.

readDirectoryWith :: (FilePath -> IO a) -> FilePath -> IO (AnchoredDirTree a)

same as readDirectory but allows us to, for example, use ByteString.readFile to return a tree of ByteStrings.

readDirectoryWithL :: (FilePath -> IO a) -> FilePath -> IO (AnchoredDirTree a)

A "lazy" version of `readDirectoryWith` that does IO operations as needed i.e. as the tree is traversed in pure code. NOTE: This function uses unsafePerformIO under the hood. I believe our use here is safe, but this function is experimental in this release:

writeDirectory :: AnchoredDirTree String -> IO (AnchoredDirTree ())

write a DirTree of strings to disk. Clobbers files of the same name. Doesn't affect files in the directories (if any already exist) with different names. Returns a new AnchoredDirTree where failures were lifted into a `Failed` constructor:

writeDirectoryWith :: (FilePath -> a -> IO b) -> AnchoredDirTree a -> IO (AnchoredDirTree b)

writes the directory structure to disk and uses the provided function to write the contents of `Files` to disk. The return value of the function will become the new `contents` of the returned, where IO errors at each node are replaced with `Failed` constructors. The returned tree can be compared to the passed tree to see what operations, if any, failed:

# Lower level functions

build :: FilePath -> IO (AnchoredDirTree FilePath)

builds a DirTree from the contents of the directory passed to it, saving the base directory in the Anchored* wrapper. Errors are caught in the tree in the Failed constructor. The `file` fields initially are populated with full paths to the files they are abstracting.

buildL :: FilePath -> IO (AnchoredDirTree FilePath)

identical to `build` but does directory reading IO lazily as needed:

openDirectory :: FilePath -> IOMode -> IO (AnchoredDirTree Handle)

a simple application of readDirectoryWith openFile:

writeJustDirs :: AnchoredDirTree a -> IO (AnchoredDirTree a)

writes the directory structure (not files) of a DirTree to the anchored directory. Returns a structure identical to the supplied tree with errors replaced by `Failed` constructors:

## Manipulating FilePaths

zipPaths :: AnchoredDirTree a -> DirTree (FilePath, a)

tuple up the complete file path with the `file` contents, by building up the path, trie-style, from the root. The filepath will be relative to "anchored" directory.

This allows us to, for example, `mapM_ uncurry writeFile` over a DirTree of strings, although `writeDirectory` does a better job of this.

free :: AnchoredDirTree a -> DirTree a

Deprecated: Use record `dirTree`

DEPRECATED. Use record `dirTree` instead.

# Utility functions

## Shape comparison and equality

equalShape :: DirTree a -> DirTree b -> Bool

Tests equality of two trees, ignoring their free variable portion. Can be used to check if any files have been added or deleted, for instance.

comparingShape :: DirTree a -> DirTree b -> Ordering

a compare function that ignores the free "file" type variable:

## Handling failure

successful :: DirTree a -> Bool

True if there are no Failed constructors in the tree

anyFailed :: DirTree a -> Bool

True if any Failed constructors in the tree

failed :: DirTree a -> Bool

returns true if argument is a `Failed` constructor:

failures :: DirTree a -> [DirTree a]

returns a list of `Failed` constructors only:

failedMap :: (FileName -> IOException -> DirTree a) -> DirTree a -> DirTree a

maps a function to convert Failed DirTrees to Files or Dirs

## Tree Manipulations

flattenDir :: DirTree a -> [DirTree a]

Flattens a `DirTree` into a (never empty) list of tree constructors. `Dir` constructors will have [] as their `contents`:

sortDir :: Ord a => DirTree a -> DirTree a

Recursively sort a directory tree according to the Ord instance

sortDirShape :: DirTree a -> DirTree a

Recursively sort a tree as in `sortDir` but ignore the file contents of a File constructor

filterDir :: (DirTree a -> Bool) -> DirTree a -> DirTree a

applies the predicate to each constructor in the tree, removing it (and its children, of course) when the predicate returns False. The topmost constructor will always be preserved:

## Navigation

dropTo :: FileName -> AnchoredDirTree a -> Maybe (AnchoredDirTree a)

If the argument is a `Dir` containing a sub-DirTree matching `FileName` then return that subtree, appending the `name` of the old root `Dir` to the `anchor` of the AnchoredDirTree wrapper. Otherwise return `Nothing`.

## Operators

(</$>) :: Functor f => (DirTree a -> DirTree b) -> f (AnchoredDirTree a) -> f (AnchoredDirTree b) infixl 4

Allows for a function on a bare DirTree to be applied to an AnchoredDirTree within a Functor. Very similar to and useful in combination with `<$>`:

# Lenses

These are compatible with the "lens" library

_contents :: Applicative f => ([DirTree a] -> f [DirTree a]) -> DirTree a -> f (DirTree a)

_err :: Applicative f => (IOException -> f IOException) -> DirTree a -> f (DirTree a)

_file :: Applicative f => (a -> f a) -> DirTree a -> f (DirTree a)

_name :: Functor f => (FileName -> f FileName) -> DirTree a -> f (DirTree a)

_anchor :: Functor f => (FilePath -> f FilePath) -> AnchoredDirTree a -> f (AnchoredDirTree a)

_dirTree :: Functor f => (DirTree t -> f (DirTree a)) -> AnchoredDirTree t -> f (AnchoredDirTree a)