Skip to content

Commit

Permalink
More preparation for Cabal, name change
Browse files Browse the repository at this point in the history
Ignore-this: e6bc8fae96ff29b5abf5e89591ed1e9a
-Changed module name to Tree, so that it will reside in System.Directory.Tree
-finished .cabal file
-other adjustments to DirectoryTree interface/exports

darcs-hash:20090508212743-a5925-b08d39e13d59b220953c689d9acd71785dbd6e54
  • Loading branch information
jberryman committed May 8, 2009
1 parent 03ee7c2 commit 4cd6652
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 64 deletions.
26 changes: 26 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Copyright (c) 2009 Brandon Simmons

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

2. 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.

THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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.
2 changes: 2 additions & 0 deletions Setup.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import Distribution.Simple
main = defaultMain
133 changes: 69 additions & 64 deletions DirectoryTree.hs → System/Directory/Tree.hs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
--------------------------------------------------------------------
-- |
-- Module : System.Directory.DirectoryTree
-- Module : System.Directory.Tree
-- Copyright : (c) Brandon Simmons
-- License : BSD3
--
Expand All @@ -20,47 +20,46 @@
--
-- 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!
--------------------------------------------------------------------



module DirectoryTree (-- high level:
readDirectory, readDirectoryWith,
writeDirectory, writeDirectoryWith,

-- lower level:
zipPaths, build, openDirectory, writeJustDirs,

-- utilities:
anyFailed, failures, failedMap, free,

-- Types:
DirTree (..), AnchoredDirTree (..), FileName)
where
module Tree (

-- * Data types for representing directory trees
DirTree (..)
, AnchoredDirTree (..)
, FileName

-- * High level IO functions
, readDirectory
, readDirectoryWith
, writeDirectory
, writeDirectoryWith

-- * Lower level functions
, zipPaths
, build
, openDirectory
, writeJustDirs

-- * Utility functions
-- ** Handling failure
, successful
, anyFailed
, failures
, failedMap
-- ** Misc.
, free

) where

{-
NOTES:
a simple interface to directories. DirTree's free variable can hold
file paths, strings to be written to files in a directory tree,
file-handles, byte-strings read with 'readFile',etc.
the AnchoredDirTree type is simply a wrapper that allows us to store,
and return the base file path of the directory.
we don't provide many special functions for only working within the
current directory. it should be easy enough to create a DirTree
"Anchored" at the current directory like so:
anchorAtCurrent dTree = "." :/ dTree
IDEAS:
an informal comonad instance might might make sense: for example, cobind
to convert Failed constructors to Files or Dirs, or cojoin to allow us to
use the traversable/foldable functions over an entire File/Failed
constructor.
define a 'cojoin' function if you want.
TODO:
- add some tests
- tree combining functions
- tree searching based on file names
- look into comonad abstraction
Expand All @@ -84,10 +83,11 @@ import qualified Data.Foldable as F



-- the String in Dir is always the directory name, never a full path. We
-- provide functions to convert a DirTree String (where String is a filename)
-- to a DirTree FilePath, where the File constructor holds a full path to a
-- file (relative or absolute) rather than a bare file name.
-- | 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'.
data DirTree a = Dir { name :: FileName,
contents :: [DirTree a] } --files + directories
| File { name :: FileName,
Expand All @@ -101,14 +101,14 @@ instance (Ord a)=> Ord (DirTree a) where
compare = compare `on` name


-- a simple wrapper to hold a base directory name, which can be either
-- | 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.
-- in the DirTree. (uses an infix constructor; don't be scared)
data AnchoredDirTree a = FilePath :/ DirTree a
deriving (Show, Ord, Eq)

-- an element in a FilePath:
-- | an element in a FilePath:
type FileName = String


Expand All @@ -132,27 +132,27 @@ instance T.Traversable DirTree where
----------------------------


-- build an AnchoredDirTree, given the path to a directory, opening the files
-- | build an AnchoredDirTree, given the path to a directory, opening the files
-- using readFile.
readDirectory :: FilePath -> IO (AnchoredDirTree String)
readDirectory = readDirectoryWith readFile

-- same as readDirectory but allows us to, for example, use ByteString.readFile
-- to return a tree of ByteStrings.
-- | same as readDirectory but allows us to, for example, use
-- ByteString.readFile to return a tree of ByteStrings.
readDirectoryWith :: (FilePath -> IO a) -> FilePath -> IO (AnchoredDirTree a)
readDirectoryWith f p = do (b:/t) <- build p
t' <- T.mapM f t
return $ b:/t'


-- 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:
-- | 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:
writeDirectory :: AnchoredDirTree String -> IO ()
writeDirectory = writeDirectoryWith writeFile

-- writes the directory structure to disc, then uses the provided function to
-- write the contents of Files to disc. For example, we can provide 'readfile'
-- for ByteStrings on a DirTree of ByteStrings.
-- | writes the directory structure to disc, then uses the provided function to
-- write the contents of Files to disc.
writeDirectoryWith :: (FilePath -> a -> IO ()) -> AnchoredDirTree a -> IO ()
writeDirectoryWith f t = do writeJustDirs t
F.mapM_ (uncurry f) (zipPaths t)
Expand All @@ -166,14 +166,14 @@ writeDirectoryWith f t = do writeJustDirs t
-----------------------------


-- a simple application of readDirectoryWith openFile:
-- | a simple application of readDirectoryWith openFile:
openDirectory :: FilePath -> IOMode -> IO (AnchoredDirTree Handle)
openDirectory p m = readDirectoryWith (flip openFile m) p



-- 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
-- | 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.
build :: FilePath -> IO (AnchoredDirTree FilePath)
Expand All @@ -198,43 +198,48 @@ build' p =






-----------------
--[ UTILITIES ]--
-----------------


-- strips away base directory wrapper:
free :: AnchoredDirTree a -> DirTree a
free (_:/t) = t




---- HANDLING FAILURES ----

-- | True if any Failed constructors in the tree
anyFailed :: DirTree a -> Bool
anyFailed = not . null . failures
anyFailed = not . successful

-- | True if there are no Failed constructors in the tree
successful :: DirTree a -> Bool
successful = null . failures

-- MAKE THIS COMONADIC???
-- returns a list of 'Failed' constructors only:

-- | returns a list of 'Failed' constructors only:
failures :: DirTree a -> [DirTree a]
failures (Dir _ cs) = concatMap failures cs
failures (File _ _) = []
failures f = [f]


-- maps a function to convert Failed DirTrees to Files or Dirs
-- | maps a function to convert Failed DirTrees to Files or Dirs
failedMap :: (FileName -> Exception -> DirTree a) -> DirTree a -> DirTree a
failedMap f (Dir n cs) = Dir n $map (failedMap f) cs
failedMap f (Failed n e) = f n e
failedMap _ fle = fle



---- OTHER ----

-- | strips away base directory wrapper:
free :: AnchoredDirTree a -> DirTree a
free (_:/t) = t



---------------
--[ HELPERS ]--
Expand All @@ -245,7 +250,7 @@ failedMap _ fle = fle



-- tuple up the complete filename with the File contents, by building up the
-- | tuple up the complete filename with the File contents, by building up the
-- path, trie-style, from the root. The filepath will be relative to the current
-- directory.
-- This allows us to, for example, mapM_ 'uncurry writeFile' over a DirTree of
Expand All @@ -267,7 +272,7 @@ baseDir = joinPath . init . splitDirectories
---- IO HELPERS: ----


-- writes the directory structure (not files) of a DirTree to the anchored
-- | writes the directory structure (not files) of a DirTree to the anchored
-- directory. can be preparation for writing files:
writeJustDirs :: AnchoredDirTree a -> IO ()
writeJustDirs (b:/t) = write' b t
Expand Down
56 changes: 56 additions & 0 deletions directory-tree.cabal
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: directory-tree
version: 0.1
homepage: http://code.haskell.org/~bsimmons/code/directory-tree
synopsis: A simple directory-like tree datatype, with useful IO functions and Foldable/Traversable instance
description: A simple directory-like tree datatype, with useful IO functions and Foldable/Traversable instance
.
Provides a simple data structure mirroring a directory tree on the
filesystem, as well as useful functions for reading and writing
file/directory structures in the IO monad.
.
Importing the library and optional (useful) Foldable and Traverable libraries:
.
> import System.Directory.Tree
> import qualified Data.Foldable as F
> import qualified Data.Traversable as T
.
Write a hand-made directory tree of textfiles (strings) to the disk.
Simulates creating a new user Tux's home directory on a unix machine:
.
> writeDirectory$ "/home" :/ Dir "Tux" [File "README" "Welcome!"]
.
"read" a directory by opening all the files at a filepath with readFile,
returning an 'AnchoredDirTree String' (d2). Then check for any IO failures:
.
> do (base :/ d2) <- readDirectory "../parent_dir/dir2/"
> let failed = anyFailed d2
> if failed then ...
.
Use Foldable instance function to concat a directory 'dir' of text files into a
single file under the same directory:
.
> do (b :/ dt) <- readDirectory dir
> let f = F.concat dt
> return$ b :/ File "ALL_TEXT" f
.
Open all the files in the current directory as lazy bytestrings, ignoring
the base path in Anchored wrapper:
.
> import qualified Data.ByteString.Lazy as B
> (_ :/ dTree) <- readDirectoryWith B.readFile "./" {- "" works too -}
.
category: Data, System
license: BSD3
license-file: LICENSE
copyright: (c) 2009, Brandon Simmons <brandon.m.simmons@gmail.com>
author: Brandon Simmons
maintainer: Brandon Simmons <brandon.m.simmons@gmail.com>
cabal-version: >= 1.2.0
build-type: Simple
tested-with: GHC ==6.8.2
extra-source-files: examples.hs


library
exposed-modules: System.Directory.Tree
build-depends: base

0 comments on commit 4cd6652

Please sign in to comment.