`hash-addressed` is a simple system for maintaining a directory wherein each
file's name is a hash of its content.
```haskell
import HashAddressed.Directory
import HashAddressed.HashFunction
```
```haskell
import qualified Data.ByteString.Lazy as Lazy
import qualified Data.ByteString as Strict
import qualified Pipes
```
### `Directory`
First define a `Directory` value by specifying which hash function to use and
the path of the directory in which the files shall be kept.
```haskell
data Directory = Directory { directoryPath :: FilePath,
hashFunction :: HashFunction }
```
Presently the only supported hash function is `sha256`.
Ensure that `directoryPath` is the path of an existing directory. You can then
write files into the directory using one of three *write* functions:
`writeLazy`, `writeStream`, and `writeExcept`.
### `writeLazy`
`writeLazy` is the simplest to use; just give it a lazy `ByteString`.
```haskell
writeLazy :: forall m. MonadIO m =>
Directory -> Lazy.ByteString -> m WriteResult
```
```haskell
data WriteResult = WriteResult{ hashAddressedFile :: FilePath,
writeType :: WriteType }
```
```haskell
data WriteType = AlreadyPresent | NewContent
```
`WriteResult` gives you the path of the file in the store, including the path of
the store itself. Because a hash-addressed store can never contain duplicate
files, writing a file has no effect if the content is already present; the
`WriteType` value indicates whether the file was actually written by this action
or was present in the store already.
### `writeStream`
The limitation of `writeLazy` is that it doesn't allow streaming. Thus enters
`writeStream`, which uses a pipes `Producer` to represent the content. The
producer can perform `IO` while generating stream content (for example, perhaps
it reads byte strings from a network socket). The producer can also return a
value (the `commit` type parameter) that will be returned alongside the
`WriteResult`.
```haskell
writeStream :: forall commit m. MonadIO m =>
Directory
-> Pipes.Producer Strict.ByteString IO commit
-> m (commit, WriteResult)
```
All operations that write into a hash-addressed `Directory` are performed by
first writing the content somewhere within the system temporary directory and
then moving the file to its target location. This ensures that the store never
makes visible the results of a partial write. If the producer throws an
exception, everything written so far will be deleted and no content will be
written to the `Directory`.
### `writeExcept`
If there is some interesting way in which your stream can fail, you may wish to
use `writeExcept` instead. In this variant, the producer returns an
`Either abort commit` indicating whether the result should be committed to the
store. Return a `Left` result to signal that an error has occurred. The
`writeExcept` action will then throw the `abort` value into a `MonadError`
context.
```haskell
writeExcept :: forall abort commit m. (MonadIO m, MonadError abort m) =>
Directory -> Pipes.Producer Strict.ByteString IO (Either abort commit)
-> m (commit, WriteResult)
```