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