packages feed

ipfs-1.2.0.0: library/Network/IPFS/Add.hs

module Network.IPFS.Add
  ( addRaw
  , addFile
  , addPath
  , addDir
  ) where

import           Network.IPFS.Local.Class    as IPFS
import           Network.IPFS.Prelude        hiding (link)

import           Data.ByteString.Lazy.Char8  as CL

import qualified System.FilePath.Glob        as Glob

import qualified RIO.ByteString.Lazy         as Lazy
import           RIO.Directory
import           RIO.FilePath
import qualified RIO.List                    as List

import qualified Network.IPFS.Internal.UTF8  as UTF8

import           Network.IPFS.Add.Error      as IPFS.Add
import           Network.IPFS.DAG.Link       as DAG.Link
import           Network.IPFS.DAG.Node.Types as DAG
import           Network.IPFS.Types          as IPFS

import           Network.IPFS.DAG            as DAG

addRaw ::
  MonadLocalIPFS m
  => Lazy.ByteString
  -> m (Either IPFS.Add.Error IPFS.CID)
addRaw raw =
  IPFS.runLocal ["add", "-HQ"] raw >>= \case
    Right result ->
      case CL.lines result of
        [cid] ->
          return . Right . mkCID . UTF8.stripN 1 . decodeUtf8Lenient $ Lazy.toStrict cid

        bad ->
          return . Left . UnexpectedOutput $ UTF8.textShow bad

    Left err ->
      return . Left . UnknownAddErr $ UTF8.textShow err

addFile ::
  MonadLocalIPFS m
  => Lazy.ByteString
  -> IPFS.Name
  -> m (Either IPFS.Add.Error (IPFS.SparseTree, IPFS.CID))
addFile raw name =
  IPFS.runLocal opts raw >>= \case
    Right result ->
      case CL.lines result of
        [inner, outer] ->
          let
            sparseTree  = Directory [(Hash rootCID, fileWrapper)]
            fileWrapper = Directory [(fileName, Content fileCID)]
            rootCID     = CID . decodeUtf8Lenient $ Lazy.toStrict outer
            fileCID     = CID . UTF8.stripN 1 . decodeUtf8Lenient $ Lazy.toStrict inner
            fileName    = Key name
          in
            return $ Right (sparseTree, rootCID)

        bad ->
          return . Left . UnexpectedOutput $ UTF8.textShow bad


    Left err ->
      return . Left . UnknownAddErr $ UTF8.textShow err

    where
      opts = [ "add"
             , "-wq"
             , "--stdin-name"
             , unName name
             ]

addPath ::
  MonadLocalIPFS m
  => FilePath
  -> m (Either IPFS.Add.Error CID)
addPath path = IPFS.runLocal ["add", "-HQ", path] "" >>= pure . \case
  Right result ->
    case CL.lines result of
      [cid] -> Right . mkCID . UTF8.stripN 1 $ UTF8.textShow cid
      bad   -> Left . UnexpectedOutput $ UTF8.textShow bad

  Left err ->
    Left . UnknownAddErr $ UTF8.textShow err

addDir ::
  ( MonadIO m
  , MonadLocalIPFS m
  )
  => IPFS.Ignored
  -> FilePath
  -> m (Either IPFS.Add.Error IPFS.CID)
addDir ignored path = doesFileExist path >>= \case
  True  -> addPath path
  False -> walkDir ignored path

walkDir ::
  ( MonadIO m
  , MonadLocalIPFS m
  )
  => IPFS.Ignored
  -> FilePath
  -> m (Either IPFS.Add.Error IPFS.CID)
walkDir ignored path = do
  files <- listDirectory path

  let
    toAdd = removeIgnored ignored files
    reducer = foldResults path ignored
    seed = Right $ Node
      { dataBlock = "CAE="
      , links = []
      }

  foldM reducer seed toAdd >>= \case
    Left err   -> return $ Left err
    Right node -> DAG.putNode node

foldResults ::
  ( MonadIO m
  , MonadLocalIPFS m
  )
  => FilePath
  -> IPFS.Ignored
  -> Either IPFS.Add.Error Node
  -> FilePath
  -> m (Either IPFS.Add.Error Node)
foldResults _ _ (Left err) _ = return $ Left err
foldResults path ignored (Right node) filename = do
  addDir ignored (path </> filename) >>= \case
    Left err ->  return $ Left err
    Right cid ->
      DAG.Link.create cid (IPFS.Name filename) >>= \case
      Left err -> return . Left $ RecursiveAddErr err
      Right link ->
        return $ Right node { links = link: links node }

removeIgnored :: IPFS.Ignored -> [FilePath] -> [FilePath]
removeIgnored ignored files = List.filter (not . matchesAny ignored) files

matchesAny :: IPFS.Ignored -> FilePath -> Bool
matchesAny globs path = List.any (\x -> Glob.match x path) globs