packages feed

hstratus-drive-0.1.0.0: src/Network/HStratus/Drive.hs

{-# LANGUAGE NamedFieldPuns #-}

{- |
Module      : Network.HStratus.Drive
Copyright   : (c) 2025 Tim Emiola
Maintainer  : Tim Emiola <adetokunbo@emio.la>
SPDX-License-Identifier: BSD3

Access iCloud Drive using an authenticated session from @hstratus-auth@.

After a successful login with 'Network.HStratus.Http.login', construct a
'DriveApi' value from the returned 'AccountData', 'Session', and 'Api'
handle, then use it to browse and download files.

@
import Network.HStratus.Http   (login, mkApi)
import Network.HStratus.Http.Endpoints (Realm (..))
import Network.HStratus.Drive

main :: IO ()
main = do
  api <- mkApi Usual
  Authenticated sess ad <- login api
  da  <- mkDriveApi ad sess api
  root <- driveRoot da
  nodes <- listFolder da (fnId root)
  print nodes
@
-}
module Network.HStratus.Drive
  ( -- * Setup
    DriveApi
  , mkDriveApi

    -- * Browsing
  , driveRoot
  , listFolder

    -- * Downloading
  , downloadFile

    -- * Mutations
  , createFolder
  , renameNode
  , deleteNode
  , uploadFile

    -- * Errors
  , DriveError (..)

    -- * Re-exports
  , module Network.HStratus.Drive.Node
  )
where

import Control.Exception (throwIO)
import qualified Data.ByteString.Lazy as LBS
import Data.Text (Text)
import Network.HStratus.Drive.Node
import Network.HStratus.Http (Api)
import Network.HStratus.Internal.Drive.Download
  ( DriveError (..)
  , execCreateFolder
  , execDeleteNode
  , execRenameNode
  , execUploadFile
  , fetchChildren
  , fetchFile
  , fetchNode
  )
import Network.HStratus.Internal.Drive.Endpoints
  ( DriveEndpoints
  , mkDriveEndpoints
  )
import Network.HStratus.Session (AccountData, Session)


{- | A bundled handle pairing a logged-in 'Api' with its drive endpoints.
Construct with 'mkDriveApi'; pass to all drive operations.
-}
data DriveApi = DriveApi
  { dApi :: !Api
  , dEp :: !DriveEndpoints
  }


-- | Pair a logged-in 'Api' with drive endpoints derived from its session data.
mkDriveApi :: AccountData -> Session -> Api -> IO DriveApi
mkDriveApi ad sess api = DriveApi api <$> mkDriveEndpoints ad sess


-- | Fetch the root folder of the main CloudDocs tree.
driveRoot :: DriveApi -> IO FolderData
driveRoot DriveApi{dApi, dEp} = do
  node <- fetchNode dApi dEp rootNodeId
  case node of
    DriveFolder fd -> pure fd
    DriveFile _ -> throwIO DriveInvalidRoot


-- | Fetch the immediate children of a folder.
listFolder :: DriveApi -> DriveNodeId -> IO [DriveNode]
listFolder DriveApi{dApi, dEp} = fetchChildren dApi dEp


-- | Download the contents of a file as a lazy 'LBS.ByteString'.
downloadFile :: DriveApi -> FileData -> IO LBS.ByteString
downloadFile DriveApi{dApi, dEp} = fetchFile dApi dEp


-- | Create a new folder inside an existing folder.
createFolder :: DriveApi -> DriveNodeId -> Text -> IO ()
createFolder DriveApi{dApi, dEp} = execCreateFolder dApi dEp


-- | Rename a node (folder or file) to a new name.
renameNode :: DriveApi -> DriveNode -> Text -> IO ()
renameNode DriveApi{dApi, dEp} = execRenameNode dApi dEp


-- | Move a node (folder or file) to the trash.
deleteNode :: DriveApi -> DriveNode -> IO ()
deleteNode DriveApi{dApi, dEp} = execDeleteNode dApi dEp


-- | Upload a file into a folder.
uploadFile :: DriveApi -> FolderData -> Text -> LBS.ByteString -> IO ()
uploadFile DriveApi{dApi, dEp} = execUploadFile dApi dEp