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 :: forall (m :: * -> *).
MonadLocalIPFS m =>
ByteString -> m (Either Error CID)
addRaw ByteString
raw =
  forall (m :: * -> *).
MonadLocalIPFS m =>
[Opt] -> ByteString -> m (Either Error ByteString)
IPFS.runLocal [Opt
"add", Opt
"-HQ"] ByteString
raw forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    Right ByteString
result ->
      case ByteString -> [ByteString]
CL.lines ByteString
result of
        [Item [ByteString]
cid] ->
          forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. b -> Either a b
Right forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> CID
mkCID forall b c a. (b -> c) -> (a -> b) -> a -> c
. Natural -> Text -> Text
UTF8.stripN Natural
1 forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Text
decodeUtf8Lenient forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString
Lazy.toStrict Item [ByteString]
cid

        [ByteString]
bad ->
          forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. a -> Either a b
Left forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Error
UnexpectedOutput forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> Text
UTF8.textShow [ByteString]
bad

    Left Error
err ->
      forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. a -> Either a b
Left forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Error
UnknownAddErr forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> Text
UTF8.textShow Error
err

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

        [ByteString]
bad ->
          forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. a -> Either a b
Left forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Error
UnexpectedOutput forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> Text
UTF8.textShow [ByteString]
bad


    Left Error
err ->
      forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. a -> Either a b
Left forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Error
UnknownAddErr forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> Text
UTF8.textShow Error
err

    where
      opts :: [Opt]
opts = [ Opt
"add"
             , Opt
"-wq"
             , Opt
"--stdin-name"
             , Name -> Opt
unName Name
name
             ]

addPath ::
  MonadLocalIPFS m
  => FilePath
  -> m (Either IPFS.Add.Error CID)
addPath :: forall (m :: * -> *).
MonadLocalIPFS m =>
Opt -> m (Either Error CID)
addPath Opt
path = forall (m :: * -> *).
MonadLocalIPFS m =>
[Opt] -> ByteString -> m (Either Error ByteString)
IPFS.runLocal [Opt
"add", Opt
"-HQ", Opt
path] ByteString
"" forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. \case
  Right ByteString
result ->
    case ByteString -> [ByteString]
CL.lines ByteString
result of
      [Item [ByteString]
cid] -> forall a b. b -> Either a b
Right forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> CID
mkCID forall b c a. (b -> c) -> (a -> b) -> a -> c
. Natural -> Text -> Text
UTF8.stripN Natural
1 forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> Text
UTF8.textShow Item [ByteString]
cid
      [ByteString]
bad   -> forall a b. a -> Either a b
Left forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Error
UnexpectedOutput forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> Text
UTF8.textShow [ByteString]
bad

  Left Error
err ->
    forall a b. a -> Either a b
Left forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Error
UnknownAddErr forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> Text
UTF8.textShow Error
err

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

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

  let
    toAdd :: [Opt]
toAdd = Ignored -> [Opt] -> [Opt]
removeIgnored Ignored
ignored [Opt]
files
    reducer :: Either Error Node -> Opt -> m (Either Error Node)
reducer = forall (m :: * -> *).
(MonadIO m, MonadLocalIPFS m) =>
Opt -> Ignored -> Either Error Node -> Opt -> m (Either Error Node)
foldResults Opt
path Ignored
ignored
    seed :: Either a Node
seed = forall a b. b -> Either a b
Right forall a b. (a -> b) -> a -> b
$ Node
      { $sel:dataBlock:Node :: Text
dataBlock = Text
"CAE="
      , $sel:links:Node :: [Link]
links = []
      }

  forall (t :: * -> *) (m :: * -> *) b a.
(Foldable t, Monad m) =>
(b -> a -> m b) -> b -> t a -> m b
foldM Either Error Node -> Opt -> m (Either Error Node)
reducer forall {a}. Either a Node
seed [Opt]
toAdd forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    Left Error
err   -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b. a -> Either a b
Left Error
err
    Right Node
node -> forall (m :: * -> *).
MonadLocalIPFS m =>
Node -> m (Either Error CID)
DAG.putNode Node
node

foldResults ::
  ( MonadIO m
  , MonadLocalIPFS m
  )
  => FilePath
  -> IPFS.Ignored
  -> Either IPFS.Add.Error Node
  -> FilePath
  -> m (Either IPFS.Add.Error Node)
foldResults :: forall (m :: * -> *).
(MonadIO m, MonadLocalIPFS m) =>
Opt -> Ignored -> Either Error Node -> Opt -> m (Either Error Node)
foldResults Opt
_ Ignored
_ (Left Error
err) Opt
_ = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b. a -> Either a b
Left Error
err
foldResults Opt
path Ignored
ignored (Right Node
node) Opt
filename = do
  forall (m :: * -> *).
(MonadIO m, MonadLocalIPFS m) =>
Ignored -> Opt -> m (Either Error CID)
addDir Ignored
ignored (Opt
path Opt -> Opt -> Opt
</> Opt
filename) forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    Left Error
err ->  forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b. a -> Either a b
Left Error
err
    Right CID
cid ->
      forall (m :: * -> *).
MonadLocalIPFS m =>
CID -> Name -> m (Either Error Link)
DAG.Link.create CID
cid (Opt -> Name
IPFS.Name Opt
filename) forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
      Left Error
err -> forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. a -> Either a b
Left forall a b. (a -> b) -> a -> b
$ Error -> Error
RecursiveAddErr Error
err
      Right Link
link ->
        forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b. b -> Either a b
Right Node
node { $sel:links:Node :: [Link]
links = Link
linkforall a. a -> [a] -> [a]
: Node -> [Link]
links Node
node }

removeIgnored :: IPFS.Ignored -> [FilePath] -> [FilePath]
removeIgnored :: Ignored -> [Opt] -> [Opt]
removeIgnored Ignored
ignored [Opt]
files = forall a. (a -> Bool) -> [a] -> [a]
List.filter (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. Ignored -> Opt -> Bool
matchesAny Ignored
ignored) [Opt]
files

matchesAny :: IPFS.Ignored -> FilePath -> Bool
matchesAny :: Ignored -> Opt -> Bool
matchesAny Ignored
globs Opt
path = forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
List.any (\Pattern
x -> Pattern -> Opt -> Bool
Glob.match Pattern
x Opt
path) Ignored
globs