packages feed

ipfs-1.0.0: README.md

# ipfs-haskell

[![Build Status](https://travis-ci.org/fission-suite/PROJECTNAME.svg?branch=master)](https://travis-ci.org/fission-suite/ipfs-haskell)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/fission-suite/blob/master/LICENSE)
[![Maintainability](https://api.codeclimate.com/v1/badges/44fb6a8a0cfd88bc41ef/maintainability)](https://codeclimate.com/github/fission-suite/ipfs-haskell/maintainability)
[![Built by FISSION](https://img.shields.io/badge/⌘-Built_by_FISSION-purple.svg)](https://fission.codes)
[![Discord](https://img.shields.io/discord/478735028319158273.svg)](https://discord.gg/zAQBDEq)
[![Discourse](https://img.shields.io/discourse/https/talk.fission.codes/topics)](https://talk.fission.codes)

Documentation: [ipfs on hackage](http://hackage.haskell.org/package/ipfs)

A library for integrating IPFS into your haskell applications. Interact with the IPFS network by shelling out to a local IPFS node or communicating via the HTTP interface of a remote node. 

# QuickStart

Define instances for `MonadLocalIPFS` and/or `MonadRemoteIPFS`. Each requires only one function:

```haskell
class Monad m => MonadRemoteIPFS m where
  runRemote :: Servant.ClientM a -> m (Either Servant.ClientError a)

class Monad m => MonadLocalIPFS m where
  runLocal ::
       [IPFS.Opt]
    -> Lazy.ByteString
    -> m (Either Process.Error Process.RawMessage)
```

We use RIO processes to shell out to a local IPFS node and Servant for HTTP requests to a remote node.

After that, simply add `MonadLocalIPFS m` as a constraint to a function and you'll be able to call IPFS within it.
For instance:
```haskell
import           Network.IPFS
import qualified Network.IPFS.Add        as IPFS
import           Network.IPFS.File.Types as File

add ::
  MonadLocalIPFS  m
  => File.Serialzed
  -> m ()
add (Serialized rawData) = IPFS.addRaw rawData >>= \case
  Right newCID -> 
    -- ...
  Left err ->
    -- ...

```

You can see example instances below:
```haskell
instance
  ( HasProcessContext cfg
  , HasLogFunc cfg
  , Has IPFS.BinPath cfg
  , Has IPFS.Timeout cfg
  )
  => MonadLocalIPFS (RIO cfg) where
    runLocal opts arg = do
      IPFS.BinPath ipfs <- view hasLens
      IPFS.Timeout secs <- view hasLens
      let opts' = ("--timeout=" <> show secs <> "s") : opts

      runProc readProcess ipfs (byteStringInput arg) byteStringOutput opts' >>= \case
        (ExitSuccess, contents, _) ->
          return $ Right contents
        (ExitFailure _, _, stdErr)
          | Lazy.isSuffixOf "context deadline exceeded" stdErr ->
              return . Left $ Process.Timeout secs
          | otherwise ->
            return . Left $ Process.UnknownErr stdErr

instance
  ( Has IPFS.URL     cfg
  , Has HTTP.Manager cfg
  )
  => MonadRemoteIPFS (RIO cfg) where
    runRemote query = do
      IPFS.URL url <- view hasLens
      manager      <- view hasLens

      url
        & mkClientEnv manager
        & runClientM query
        & liftIO
```