packages feed

hs-asapo (empty) → 0.9.0

raw patch · 16 files changed

+3730/−0 lines, 16 filesdep +basedep +bytestringdep +clockbuild-type:Customsetup-changed

Dependencies added: base, bytestring, clock, doctest, hs-asapo, optparse-applicative, text, time, timerep

Files

+ ChangeLog view
@@ -0,0 +1,8 @@+2024-08-16  Philipp Middendorf  <philipp.middendorf@desy.de>++	* Add documentation and high-level bindings++2024-08-08  Philipp Middendorf  <philipp.middendorf@desy.de>++	* Initial work on 0.9.0, just the raw bindings are here+
+ LICENSE view
@@ -0,0 +1,19 @@+Copyright (c) 2024 Deutsches Elektronen-Synchrotron (DESY)++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,6 @@+module Main where++import Distribution.Extra.Doctest (defaultMainWithDoctests)++main :: IO ()+main = defaultMainWithDoctests "doctests"
+ app/SimpleConsumer.hs view
@@ -0,0 +1,93 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE OverloadedStrings #-}++module Main (main) where++import Asapo.Consumer+import Control.Applicative (Applicative ((<*>)))+import Control.Monad (forM_, (=<<), (>>=))+import Data.Bool (Bool (True))+import Data.Either (Either (Left, Right))+import Data.Function (($))+import Data.Functor ((<$>))+import Data.Maybe (Maybe (Nothing), fromMaybe)+import Data.Semigroup (Semigroup ((<>)))+import Data.Text (Text, pack)+import Data.Text.Encoding (decodeUtf8)+import qualified Data.Text.IO as TIO+import Data.Time.Clock (secondsToNominalDiffTime)+import qualified Options.Applicative as Opt+import System.IO (IO)+import Text.Show (Show (show))+import Prelude ()++hstoken :: Token+hstoken = Token "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjk1NzE3MTAyMTYsImp0aSI6Ind0ZmlzdGhpcyIsInN1YiI6ImJ0X2FzYXBvX3Rlc3QiLCJFeHRyYUNsYWltcyI6eyJBY2Nlc3NUeXBlcyI6WyJ3cml0ZSIsIndyaXRlcmF3IiwicmVhZCJdfX0.cz6R_kVf4yh7IJD6bJjDdgTaxPN3txudZx9DE6WaTtk"++data Options = Options+  { optionsServerName :: Text,+    optionsWithFilesystem :: Bool+  }++optionsParser :: Opt.Parser Options+optionsParser =+  Options+    <$> Opt.strOption (Opt.long "server-name")+    <*> Opt.switch (Opt.long "with-filesystem")++main :: IO ()+main = realMain =<< Opt.execParser opts+  where+    opts =+      Opt.info+        (optionsParser Opt.<**> Opt.helper)+        ( Opt.fullDesc+            <> Opt.progDesc "Consume data from asapo"+            <> Opt.header "simple-consumer - a simple message sender"+        )++realMain :: Options -> IO ()+realMain (Options serverName withFilesystem) = do+  withConsumer+    (ServerName serverName)+    (SourcePath "")+    (if withFilesystem then WithFilesystem else WithoutFilesystem)+    ( SourceCredentials+        { sourceType = RawSource,+          instanceId = InstanceId "auto",+          pipelineStep = PipelineStep "ps1",+          beamtime = Beamtime "asapo_test",+          beamline = Beamline "",+          dataSource = DataSource "asapo_source",+          token = hstoken+        }+    )+    \consumer -> do+      TIO.putStrLn "inited consumer"++      TIO.putStrLn "misc: setting timeout"+      setTimeout consumer (secondsToNominalDiffTime 0.5)++      getBeamtimeMeta consumer >>= \meta -> TIO.putStrLn $ "beamtime metadata: " <> (fromMaybe "N/A" meta)++      TIO.putStrLn "listing all available streams:"+      streams <- getStreamList consumer Nothing FilterAllStreams+      forM_ streams \stream -> do+        TIO.putStrLn $ "=> stream info " <> pack (show stream)+        streamSize <- getCurrentSize consumer (streamInfoName stream)+        TIO.putStrLn $ "   stream size: " <> pack (show streamSize)+        datasetCount <- getCurrentDatasetCount consumer (streamInfoName stream) IncludeIncomplete+        TIO.putStrLn $ "   dataset count: " <> pack (show datasetCount)++      -- withGroupId consumer outputError \groupId -> do+      --   onSuccess "getNextMessageMeta" (getNextMessageMeta consumer (streamInfoName stream) groupId) \(messageMetaHandle, messageMeta) -> do+      --     TIO.putStrLn "   got message meta"+      --   onSuccess "getNextMessageMetaAndData" (getNextMessageMetaAndData consumer (streamInfoName stream) groupId) \(messageMetaHandle, messageMeta, messageData) -> do+      --     TIO.putStrLn "   got message"++      (meta, data') <- getMessageMetaAndDataById consumer (StreamName "default") (messageIdFromInt 156)+      TIO.putStrLn $ "meta: " <> pack (show meta)+      TIO.putStrLn $ "data: " <> decodeUtf8 data'++      TIO.putStrLn "misc: resending nacs"+      resendNacs consumer True (secondsToNominalDiffTime 1) 10
+ app/SimpleProducer.hs view
@@ -0,0 +1,89 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE OverloadedStrings #-}++module Main (main) where++import Asapo.Producer+import Control.Applicative (Applicative ((<*>)))+import Control.Monad (void, when, (=<<))+import Data.Bits ((.|.))+import Data.Either (Either (Left, Right))+import Data.Function (($))+import Data.Functor ((<$>))+import Data.Semigroup (Semigroup ((<>)))+import Data.Text (Text, pack)+import Data.Text.Encoding (encodeUtf8)+import qualified Data.Text.IO as TIO+import Data.Time.Clock (secondsToNominalDiffTime)+import Data.Word (Word64)+import Foreign.C.String (newCString, withCString)+import qualified Options.Applicative as Opt+import System.IO (IO)+import Text.Show (Show (show))+import Prelude ()++hstoken :: Token+hstoken = Token "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjk1NzE3MTAyMTYsImp0aSI6Ind0ZmlzdGhpcyIsInN1YiI6ImJ0X2FzYXBvX3Rlc3QiLCJFeHRyYUNsYWltcyI6eyJBY2Nlc3NUeXBlcyI6WyJ3cml0ZSIsIndyaXRlcmF3IiwicmVhZCJdfX0.cz6R_kVf4yh7IJD6bJjDdgTaxPN3txudZx9DE6WaTtk"++data Options = Options+  { optionsMessageId :: Word64,+    optionsMessageContent :: Text,+    optionsEndpoint :: Text,+    optionsStreamName :: Text+  }++optionsParser :: Opt.Parser Options+optionsParser =+  Options+    <$> Opt.option Opt.auto (Opt.long "message-id" <> Opt.help "ID for the message to send (don't re-use, because of duplication messages)")+    <*> Opt.strOption (Opt.long "message-content" <> Opt.help "what to put into the message")+    <*> Opt.strOption (Opt.long "endpoint" <> Opt.help "asapo endpoint")+    <*> Opt.strOption (Opt.long "stream-name")++main :: IO ()+main = realMain =<< Opt.execParser opts+  where+    opts =+      Opt.info+        (optionsParser Opt.<**> Opt.helper)+        ( Opt.fullDesc+            <> Opt.progDesc "Send a string with a specified ID"+            <> Opt.header "simple-producer - a simple message sender"+        )++realMain :: Options -> IO ()+realMain (Options messageId messageContent endpoint streamName) = void $+  withProducer+    (Endpoint endpoint)+    (ProcessingThreads 1)+    TcpHandler+    ( SourceCredentials+        { sourceType = RawSource,+          instanceId = InstanceId "test_instance",+          pipelineStep = PipelineStep "pipeline_step_1",+          beamtime = Beamtime "asapo_test",+          beamline = Beamline "auto",+          dataSource = DataSource "asapo_source",+          token = hstoken+        }+    )+    (secondsToNominalDiffTime 10)+    \producer -> do+      TIO.putStrLn "connected, sending data"+      let responseHandler :: RequestResponse -> IO ()+          responseHandler requestResponse = TIO.putStrLn $ "in response handler, payload " <> responsePayload requestResponse <> ", error " <> pack (show (responseError requestResponse))+      void $+        send+          producer+          (MessageId messageId)+          (FileName $ "raw/" <> streamName <> "/" <> pack (show messageId) <> ".txt")+          (Metadata "{\"test\": 3.0}")+          (DatasetSubstream 0)+          (DatasetSize 0)+          NoAutoId+          (encodeUtf8 messageContent)+          DataAndMetadata+          FilesystemAndDatabase+          (StreamName streamName)+          responseHandler+      waitRequestsFinished producer (secondsToNominalDiffTime 10)
+ hs-asapo.cabal view
@@ -0,0 +1,108 @@+cabal-version:  3.4+name:           hs-asapo+version:        0.9.0+synopsis:       Haskell bindings for ASAP:O+category:       System, FFI, Distributed Computing+homepage:       https://github.com/pmiddend/hs-asapo+bug-reports:    https://github.com/pmiddend/hs-asapo/issues+author:         Philipp Middendorf+maintainer:     philipp.middendorf@desy.de+copyright:      2024 Philipp Middendorf+license:        MIT+license-file:   LICENSE+build-type:     Custom+extra-source-files: ChangeLog+description:+  Haskell bindings for ASAP:O, a middleware platform for high-performance data analysis. Some general notes about this project:+  +  * @newtype@ and enumerations are used liberally, to make function calls more readable and requiring less documentation (cf. <https://yveskalume.dev/boolean-blindness-dont-represent-state-with-boolean boolean blindness>)+  * For text, such as URLs, identifiers, we assume UTF-8 and use strict [Text]("Data.Text")+  * For data, we copy the data into a strict [ByteString]("Data.ByteString")++  There are two interfaces available: one which does not throw exceptions, but returns a @Either Error a@, and one which throws exceptions. Both expose the same functions. It's yours to decide which one to use.++source-repository head+  type: git+  location: https://github.com/pmiddend/hs-asapo++common warnings+    ghc-options: -Weverything -Wno-missing-safe-haskell-mode -Wno-unsafe -Wno-missing-deriving-strategies -Wno-all-missed-specialisations -Wno-monomorphism-restriction -Wno-safe -Wno-missing-local-signatures -Wno-prepositive-qualified-module -Wno-missing-kind-signatures -Wno-missed-specializations++custom-setup+  setup-depends:+      base < 5+    , cabal-doctest >= 1.0.9 && <1.1++library+  import: warnings+  exposed-modules: Asapo.Raw.Common+                 , Asapo.Raw.Consumer+                 , Asapo.Raw.Producer+                 , Asapo.Raw.FreeHandleHack+                 , Asapo.Either.Producer+                 , Asapo.Either.Consumer+                 , Asapo.Either.Common+                 , Asapo.Producer+                 , Asapo.Consumer+  build-depends:+      base >=4.7 && <5+    , text >= 2.0.2 && < 2.1+    -- for timespec data type (is Storable)+    , clock >= 0.8.4 && < 0.9+    -- for higher-level UTCTime/LocalTime+    , time >= 1.12.2 && < 1.13+    -- for RFC3339+    , timerep >= 2.1.0 && < 2.2+    -- for the HL interface to send+    , bytestring  >= 0.11.5 && < 0.12++  pkgconfig-depends: libasapo-consumer+                   , libasapo-producer+  hs-source-dirs:    lib++-- Marked as "benchmark" so the dependencies on hackage are accurate+benchmark simple-producer+  type: exitcode-stdio-1.0+  main-is: SimpleProducer.hs+  +  ghc-options: -threaded -rtsopts -with-rtsopts=-N+  hs-source-dirs:+      app+  build-depends:+      base >=4.7 && <5+    , hs-asapo+    , text >= 2.0.2 && < 2.1+    , time >= 1.12.2 && < 1.13+    , optparse-applicative >= 0.18.1 && < 0.19+    +-- Marked as "benchmark" so the dependencies on hackage are accurate+benchmark simple-consumer+  type: exitcode-stdio-1.0+  main-is: SimpleConsumer.hs+  +  ghc-options: -threaded -rtsopts -with-rtsopts=-N+  hs-source-dirs:+      app+  build-depends:+      base >=4.7 && <5+    , hs-asapo+    , text >= 2.0.2 && < 2.1+    , time >= 1.12.2 && < 1.13+    , optparse-applicative+    +-- copied from https://github.com/ulidtko/cabal-doctest+test-suite doctests+  type:             exitcode-stdio-1.0+  main-is:          doctests.hs+  build-depends:+      base >=4.7 && <5+    , doctest >=0.15   && <1+    , clock >= 0.8.4 && < 0.9+    , timerep >= 2.1.0 && < 2.2+    , time >= 1.12.2 && < 1.13+    , bytestring  >= 0.11.5 && < 0.12+    , text >= 2.0.2 && < 2.1+    , hs-asapo++  ghc-options:      -Wall -threaded+  hs-source-dirs:   tests
+ lib/Asapo/Consumer.hs view
@@ -0,0 +1,457 @@+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS_GHC -Wno-missing-import-lists #-}++-- |+-- Description : High-level interface for all consumer-related functions, using exceptions instead of @Either@+--+-- To implement an ASAP:O consumer, you should only need this interface.+-- It exposes no memory-management functions (like free) or pointers, and+-- is thus safe to use.+--+-- = Simple Example+--+-- Here's some code for a simple consumer that connects, outputs the available streams, and then gets a specific message by ID with metadata and data:+--+-- >>> :seti -XOverloadedStrings+-- >>> :{+--  module Main where+--  import Asapo.Consumer+--  import Prelude hiding (putStrLn)+--  import Data.Maybe(fromMaybe)+--  import Control.Monad(forM_)+--  import Data.Text(pack)+--  import Data.Text.IO(putStrLn)+--  import Data.Text.Encoding(decodeUtf8)+--  import qualified Data.ByteString as BS+--  main :: IO ()+--  main =+--    withConsumer+--      (ServerName "localhost:8040")+--      (SourcePath "")+--      WithoutFilesystem+--      ( SourceCredentials+--        { sourceType = RawSource,+--          instanceId = InstanceId "auto",+--          pipelineStep = PipelineStep "ps1",+--          beamtime = Beamtime "asapo_test",+--          beamline = Beamline "",+--          dataSource = DataSource "asapo_source",+--          token = Token "token-please-change"+--        }+--      ) $ \consumer -> do+--        beamtimeMeta <- getBeamtimeMeta consumer+--        putStrLn $ "beamtime metadata: " <> (fromMaybe "N/A" beamtimeMeta)+--        streams <- getStreamList consumer Nothing FilterAllStreams+--        forM_ streams $ \stream -> do+--          putStrLn $ "=> stream info " <> pack (show stream)+--          streamSize <- getCurrentSize consumer (streamInfoName stream)+--          putStrLn $ "   stream size: " <> pack (show streamSize)+--          datasetCount <- getCurrentDatasetCount consumer (streamInfoName stream) IncludeIncomplete+--          putStrLn $ "   dataset count: " <> pack (show datasetCount)+--        metaAndData <- getMessageMetaAndDataById consumer (StreamName "default") (messageIdFromInt 1337)+--        putStrLn $ "meta: " <> pack (show (fst metaAndData))+--        putStrLn $ "data: " <> decodeUtf8 (snd metaAndData)+-- :}+module Asapo.Consumer+  ( -- * Error types+    SomeConsumerException,+    NoData,+    EndOfStream,+    StreamFinished,+    UnavailableService,+    InterruptedTransaction,+    LocalIOError,+    WrongInput,+    PartialData,+    UnsupportedClient,+    DataNotInCache,+    UnknownError,++    -- * Types+    Consumer,+    Dataset (..),+    MessageMetaHandle,+    DeleteFlag (..),+    ErrorOnNotExistFlag (..),+    ErrorType (ErrorNoData),+    FilesystemFlag (..),+    PipelineStep (..),+    Beamtime (Beamtime),+    DataSource (DataSource),+    Beamline (Beamline),+    StreamInfo (..),+    GroupId,+    IncludeIncompleteFlag (..),+    MessageMeta (..),+    ServerName (..),+    SourcePath (..),+    SourceType (..),+    StreamName (..),+    InstanceId (..),+    Token (..),+    StreamFilter (..),+    SourceCredentials (..),+    NetworkConnectionType (..),++    -- * Initialization+    withConsumer,+    withGroupId,++    -- * Getters+    getCurrentSize,+    getCurrentDatasetCount,+    getBeamtimeMeta,+    getNextDataset,+    getLastDataset,+    getLastDatasetInGroup,+    getMessageMetaAndDataById,+    getMessageMetaById,+    getMessageDataById,+    getNextMessageMetaAndData,+    getNextMessageMeta,+    getNextMessageData,+    getLastMessageMetaAndData,+    getLastMessageMeta,+    getLastMessageData,+    getLastInGroupMessageMetaAndData,+    getLastInGroupMessageMeta,+    getLastInGroupMessageData,+    getUnacknowledgedMessages,+    getCurrentConnectionType,+    getStreamList,+    messageIdFromInt,+    queryMessages,+    retrieveDataForMessageMeta,++    -- * Modifiers+    resetLastReadMarker,+    setTimeout,+    setLastReadMarker,+    setStreamPersistent,+    acknowledge,+    negativeAcknowledge,+    deleteStream,+    resendNacs,+  )+where++import Asapo.Either.Common+  ( Beamline (Beamline),+    Beamtime (Beamtime),+    DataSource (DataSource),+    InstanceId (..),+    MessageId,+    PipelineStep (..),+    SourceCredentials (..),+    SourceType (..),+    StreamInfo (..),+    StreamName (..),+    Token (..),+    messageIdFromInt,+  )+import Asapo.Either.Consumer+  ( Consumer,+    Dataset (..),+    DeleteFlag (..),+    Error (Error),+    ErrorOnNotExistFlag (..),+    ErrorType (..),+    FilesystemFlag (..),+    GroupId,+    IncludeIncompleteFlag (..),+    MessageMeta (..),+    MessageMetaHandle,+    NetworkConnectionType (..),+    ServerName (..),+    SourcePath (..),+    StreamFilter (..),+    getCurrentConnectionType,+    resendNacs,+    setTimeout,+  )+import qualified Asapo.Either.Consumer as PC+import Control.Applicative (pure)+import Control.Exception (Exception (fromException, toException), SomeException, throw)+import Control.Monad (Monad)+import qualified Data.ByteString as BS+import Data.Either (Either (Left, Right))+import Data.Function ((.))+import Data.Int (Int)+import Data.Maybe (Maybe)+import Data.Text (Text)+import Data.Time (NominalDiffTime)+import Data.Typeable (cast)+import Data.Word (Word64)+import System.IO (IO)+import Text.Show (Show, show)+import Prelude ()++-- $setup+-- >>> :seti -XOverloadedStrings+-- >>> import Control.Exception(catch)+-- >>> import Prelude(undefined, (<>), error)+-- >>> import Data.Text.IO(putStrLn)+-- >>> import Data.Text(pack)+-- >>> let consumer = undefined++-- | Parent class for all consumer-related exceptions. This makes catchall possible, as in...+--+-- @+-- setStreamPersistent consumer (StreamName "default")+--   `catch` (\e -> error ("Caught " <> (show (e :: SomeConsumerException))))+-- @+data SomeConsumerException+  = forall e. (Exception e) => SomeConsumerException e++instance Show SomeConsumerException where+  show (SomeConsumerException e) = show e++instance Exception SomeConsumerException++consumerExceptionToException :: (Exception e) => e -> SomeException+consumerExceptionToException = toException . SomeConsumerException++consumerExceptionFromException :: (Exception e) => SomeException -> Maybe e+consumerExceptionFromException x = do+  SomeConsumerException a <- fromException x+  cast a++newtype NoData = NoData Text deriving (Show)++instance Exception NoData where+  toException = consumerExceptionToException+  fromException = consumerExceptionFromException++newtype EndOfStream = EndOfStream Text deriving (Show)++instance Exception EndOfStream where+  toException = consumerExceptionToException+  fromException = consumerExceptionFromException++newtype StreamFinished = StreamFinished Text deriving (Show)++instance Exception StreamFinished where+  toException = consumerExceptionToException+  fromException = consumerExceptionFromException++newtype UnavailableService = UnavailableService Text deriving (Show)++instance Exception UnavailableService where+  toException = consumerExceptionToException+  fromException = consumerExceptionFromException++newtype InterruptedTransaction = InterruptedTransaction Text deriving (Show)++instance Exception InterruptedTransaction where+  toException = consumerExceptionToException+  fromException = consumerExceptionFromException++newtype LocalIOError = LocalIOError Text deriving (Show)++instance Exception LocalIOError where+  toException = consumerExceptionToException+  fromException = consumerExceptionFromException++newtype WrongInput = WrongInput Text deriving (Show)++instance Exception WrongInput where+  toException = consumerExceptionToException+  fromException = consumerExceptionFromException++newtype PartialData = PartialData Text deriving (Show)++instance Exception PartialData where+  toException = consumerExceptionToException+  fromException = consumerExceptionFromException++newtype UnsupportedClient = UnsupportedClient Text deriving (Show)++instance Exception UnsupportedClient where+  toException = consumerExceptionToException+  fromException = consumerExceptionFromException++newtype DataNotInCache = DataNotInCache Text deriving (Show)++instance Exception DataNotInCache where+  toException = consumerExceptionToException+  fromException = consumerExceptionFromException++newtype UnknownError = UnknownError Text deriving (Show)++instance Exception UnknownError where+  toException = consumerExceptionToException+  fromException = consumerExceptionFromException++errorTypeToException :: ErrorType -> Text -> a+errorTypeToException ErrorNoData = throw . NoData+errorTypeToException ErrorEndOfStream = throw . EndOfStream+errorTypeToException ErrorStreamFinished = throw . StreamFinished+errorTypeToException ErrorUnavailableService = throw . UnavailableService+errorTypeToException ErrorInterruptedTransaction = throw . InterruptedTransaction+errorTypeToException ErrorLocalIOError = throw . LocalIOError+errorTypeToException ErrorWrongInput = throw . WrongInput+errorTypeToException ErrorPartialData = throw . PartialData+errorTypeToException ErrorUnsupportedClient = throw . UnsupportedClient+errorTypeToException ErrorDataNotInCache = throw . DataNotInCache+errorTypeToException ErrorUnknownError = throw . UnknownError++-- | Create a consumer and do something with it. This is the main entrypoint into the consumer+withConsumer :: forall a. ServerName -> SourcePath -> FilesystemFlag -> SourceCredentials -> (Consumer -> IO a) -> IO a+withConsumer serverName sourcePath filesystemFlag creds onSuccess =+  let onError (Error errorMessage errorType) = errorTypeToException errorType errorMessage+   in PC.withConsumer serverName sourcePath filesystemFlag creds onError onSuccess++-- | Allocate a group ID and call a callback+withGroupId :: forall a. Consumer -> (GroupId -> IO a) -> IO a+withGroupId consumer onSuccess =+  let onError (Error errorMessage errorType) = errorTypeToException errorType errorMessage+   in PC.withGroupId consumer onError onSuccess++maybeThrow :: (Monad m) => m (Either Error b) -> m b+maybeThrow f = do+  result <- f+  case result of+    Left (Error errorMessage errorType) -> errorTypeToException errorType errorMessage+    Right v -> pure v++-- | Reset the last read marker for the stream+resetLastReadMarker :: Consumer -> GroupId -> StreamName -> IO Int+resetLastReadMarker consumer groupId streamName = maybeThrow (PC.resetLastReadMarker consumer groupId streamName)++-- | Set the last read marker for the stream+setLastReadMarker :: Consumer -> GroupId -> StreamName -> MessageId -> IO Int+setLastReadMarker consumer groupId streamName value = maybeThrow (PC.setLastReadMarker consumer groupId streamName value)++-- | Acknowledge a specific message+acknowledge :: Consumer -> GroupId -> StreamName -> MessageId -> IO Int+acknowledge consumer groupId streamName messageId = maybeThrow (PC.acknowledge consumer groupId streamName messageId)++-- | Negatively acknowledge a specific message+negativeAcknowledge ::+  Consumer ->+  GroupId ->+  StreamName ->+  MessageId ->+  -- | delay+  NominalDiffTime ->+  IO Int+negativeAcknowledge consumer groupId streamName messageId delay = maybeThrow (PC.negativeAcknowledge consumer groupId streamName messageId delay)++-- | Get a list of all unacknowledged message IDs in a range+getUnacknowledgedMessages :: Consumer -> GroupId -> StreamName -> (MessageId, MessageId) -> IO [MessageId]+getUnacknowledgedMessages consumer groupId streamName (from, to) = maybeThrow (PC.getUnacknowledgedMessages consumer groupId streamName (from, to))++-- | Retrieve the list of streams with metadata+getStreamList :: Consumer -> Maybe StreamName -> StreamFilter -> IO [StreamInfo]+getStreamList consumer streamName filter = maybeThrow (PC.getStreamList consumer streamName filter)++-- | Delete a given stream+deleteStream :: Consumer -> StreamName -> DeleteFlag -> ErrorOnNotExistFlag -> IO Int+deleteStream consumer streamName deleteFlag errorOnNotExistFlag = maybeThrow (PC.deleteStream consumer streamName deleteFlag errorOnNotExistFlag)++-- | Set a stream persistent+setStreamPersistent :: Consumer -> StreamName -> IO Int+setStreamPersistent consumer streamName = maybeThrow (PC.setStreamPersistent consumer streamName)++-- | Get the current size (number of messages) of the stream+getCurrentSize :: Consumer -> StreamName -> IO Int+getCurrentSize consumer streamName = maybeThrow (PC.getCurrentSize consumer streamName)++-- | Get number of datasets in stream+getCurrentDatasetCount :: Consumer -> StreamName -> IncludeIncompleteFlag -> IO Int+getCurrentDatasetCount consumer streamName inludeIncomplete = maybeThrow (PC.getCurrentDatasetCount consumer streamName inludeIncomplete)++-- | Get beamtime metadata (which can be not set, in which case @Nothing@ is returned)+getBeamtimeMeta :: Consumer -> IO (Maybe Text)+getBeamtimeMeta consumer = maybeThrow (PC.getBeamtimeMeta consumer)++-- | Get the next dataset for a stream+getNextDataset ::+  Consumer ->+  GroupId ->+  -- | minimum size+  Word64 ->+  StreamName ->+  IO Dataset+getNextDataset consumer groupId minSize streamName = maybeThrow (PC.getNextDataset consumer groupId minSize streamName)++-- | Get the last dataset in the stream+getLastDataset ::+  Consumer ->+  -- | minimum size+  Word64 ->+  StreamName ->+  IO Dataset+getLastDataset consumer minSize streamName = maybeThrow (PC.getLastDataset consumer minSize streamName)++-- | Get the last data ste in the given group+getLastDatasetInGroup ::+  Consumer ->+  GroupId ->+  -- | minimum size+  Word64 ->+  StreamName ->+  IO Dataset+getLastDatasetInGroup consumer groupId minSize streamName = maybeThrow (PC.getLastDatasetInGroup consumer groupId minSize streamName)++-- | Given a message ID, retrieve both metadata and data+getMessageMetaAndDataById :: Consumer -> StreamName -> MessageId -> IO (MessageMeta, BS.ByteString)+getMessageMetaAndDataById consumer streamName messageId = maybeThrow (PC.getMessageMetaAndDataById consumer streamName messageId)++-- | Given a message ID, retrieve only the metadata (you can get the data later with 'retrieveDataFromMessageMeta')+getMessageMetaById :: Consumer -> StreamName -> MessageId -> IO MessageMeta+getMessageMetaById consumer streamName messageId = maybeThrow (PC.getMessageMetaById consumer streamName messageId)++-- | Given a message ID, retrieve only the data+getMessageDataById :: Consumer -> StreamName -> MessageId -> IO BS.ByteString+getMessageDataById consumer streamName messageId = maybeThrow (PC.getMessageDataById consumer streamName messageId)++-- | Retrieve the last message in the stream, with data and metadata+getLastMessageMetaAndData :: Consumer -> StreamName -> IO (MessageMeta, BS.ByteString)+getLastMessageMetaAndData consumer streamName = maybeThrow (PC.getLastMessageMetaAndData consumer streamName)++-- | Retrieve the last message in the stream, only metadata (you can get the data later with 'retrieveDataFromMessageMeta')+getLastMessageMeta :: Consumer -> StreamName -> IO MessageMeta+getLastMessageMeta consumer streamName = maybeThrow (PC.getLastMessageMeta consumer streamName)++-- | Retrieve the last message in the stream, only data+getLastMessageData :: Consumer -> StreamName -> IO BS.ByteString+getLastMessageData consumer streamName = maybeThrow (PC.getLastMessageData consumer streamName)++-- | Retrieve the last message in a given stream and group, with data and metadata+getLastInGroupMessageMetaAndData :: Consumer -> StreamName -> GroupId -> IO (MessageMeta, BS.ByteString)+getLastInGroupMessageMetaAndData consumer streamName groupId = maybeThrow (PC.getLastInGroupMessageMetaAndData consumer streamName groupId)++-- | Retrieve the last message in a given stream and group, only metadata (you can get the data later with 'retrieveDataFromMessageMeta')+getLastInGroupMessageMeta :: Consumer -> StreamName -> GroupId -> IO MessageMeta+getLastInGroupMessageMeta consumer streamName groupId = maybeThrow (PC.getLastInGroupMessageMeta consumer streamName groupId)++-- | Retrieve the last message in a given stream and group, only data+getLastInGroupMessageData :: Consumer -> StreamName -> GroupId -> IO BS.ByteString+getLastInGroupMessageData consumer streamName groupId = maybeThrow (PC.getLastInGroupMessageData consumer streamName groupId)++-- | Retrieve the next message in the stream and group, with data and metadata+getNextMessageMetaAndData :: Consumer -> StreamName -> GroupId -> IO (MessageMeta, BS.ByteString)+getNextMessageMetaAndData consumer streamName groupId = maybeThrow (PC.getNextMessageMetaAndData consumer streamName groupId)++-- | Retrieve the next message in the stream and group, only metadata (you can get the data later with 'retrieveDataFromMessageMeta')+getNextMessageMeta :: Consumer -> StreamName -> GroupId -> IO MessageMeta+getNextMessageMeta consumer streamName groupId = maybeThrow (PC.getNextMessageMeta consumer streamName groupId)++-- | Retrieve the next message in the stream and group, only data+getNextMessageData :: Consumer -> StreamName -> GroupId -> IO BS.ByteString+getNextMessageData consumer streamName groupId = maybeThrow (PC.getNextMessageData consumer streamName groupId)++-- | Query messages, return handles without data+queryMessages ::+  Consumer ->+  -- | Actual query string, see the docs for syntax+  Text ->+  StreamName ->+  IO [MessageMeta]+queryMessages consumer query streamName = maybeThrow (PC.queryMessages consumer query streamName)++-- | Retrieve actual data for the handle+retrieveDataForMessageMeta :: Consumer -> MessageMeta -> IO BS.ByteString+retrieveDataForMessageMeta consumer meta = maybeThrow (PC.retrieveDataForMessageMeta consumer meta)
+ lib/Asapo/Either/Common.hs view
@@ -0,0 +1,198 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE NamedFieldPuns #-}++module Asapo.Either.Common+  ( SourceType (..),+    InstanceId (..),+    PipelineStep (..),+    Beamline (..),+    withPtr,+    Beamtime (..),+    messageIdFromInt,+    MessageId (..),+    stringHandleToText,+    DataSource (..),+    Token (..),+    timespecToUTC,+    SourceCredentials (..),+    withCredentials,+    withText,+    StreamName (..),+    withCStringN,+    withCStringNToText,+    withConstText,+    withConstCString,+    peekConstCStringText,+    peekCStringText,+    stringHandleToTextUnsafe,+    nominalDiffToMillis,+    retrieveStreamInfoFromC,+    StreamInfo (..),+  )+where++-- \|+-- Description : Utility module with common definitions+--+-- You shouldn't need to explicitly import anything from here+import Asapo.Raw.Common (AsapoSourceCredentialsHandle, AsapoStreamInfoHandle, AsapoStringHandle (AsapoStringHandle), ConstCString, asapo_create_source_credentials, asapo_free_source_credentials, asapo_stream_info_get_finished, asapo_stream_info_get_last_id, asapo_stream_info_get_name, asapo_stream_info_get_next_stream, asapo_stream_info_get_timestamp_created, asapo_stream_info_get_timestamp_last_entry, asapo_string_c_str, kProcessed, kRaw)+import Control.Applicative (pure)+import Control.Exception (bracket)+import Control.Monad (Monad ((>>=)), (>=>))+import Data.Bool (Bool, otherwise)+import Data.Eq ((==))+import Data.Function (($), (.))+import Data.Functor ((<$>))+import Data.Int (Int)+import Data.Maybe (Maybe (Just, Nothing), fromJust)+import Data.Ord ((>))+import Data.String (String)+import Data.Text (Text, pack, unpack)+import Data.Time (NominalDiffTime, zonedTimeToUTC)+import Data.Time.Clock (UTCTime, addUTCTime)+import qualified Data.Time.RFC3339 as RFC3339+import Data.Word (Word64)+import Foreign (Ptr, Storable (peek), nullPtr, with)+import Foreign.C (CChar)+import Foreign.C.ConstPtr (ConstPtr (ConstPtr, unConstPtr))+import Foreign.C.String (CString, peekCString, withCString)+import Foreign.Marshal (alloca, mallocArray)+import Foreign.Marshal.Alloc (free)+import System.Clock (TimeSpec, toNanoSecs)+import System.IO (IO)+import Text.Show (Show)+import Prelude (Fractional ((/)), Integral, Num (fromInteger, (*)), RealFrac (round), fromIntegral)++withText :: Text -> (CString -> IO a) -> IO a+withText t = withCString (unpack t)++nominalDiffToMillis :: (Integral a) => NominalDiffTime -> a+nominalDiffToMillis = round . (* 1000)++newtype MessageId = MessageId Word64 deriving (Show)++messageIdFromInt :: (Integral a) => a -> MessageId+messageIdFromInt = MessageId . fromIntegral++newtype StreamName = StreamName Text deriving (Show)++data SourceType = RawSource | ProcessedSource++newtype InstanceId = InstanceId Text++newtype PipelineStep = PipelineStep Text++newtype Beamtime = Beamtime Text++newtype Beamline = Beamline Text++newtype DataSource = DataSource Text++newtype Token = Token Text++data SourceCredentials = SourceCredentials+  { sourceType :: SourceType,+    instanceId :: InstanceId,+    pipelineStep :: PipelineStep,+    beamtime :: Beamtime,+    beamline :: Beamline,+    dataSource :: DataSource,+    token :: Token+  }++withCredentials :: SourceCredentials -> (AsapoSourceCredentialsHandle -> IO a) -> IO a+withCredentials+  ( SourceCredentials+      { sourceType,+        instanceId = InstanceId instanceId',+        pipelineStep = PipelineStep pipelineStep',+        beamtime = Beamtime beamtime',+        beamline = Beamline beamline',+        dataSource = DataSource dataSource',+        token = Token token'+      }+    )+  f = do+    let convertSourceType RawSource = kRaw+        convertSourceType ProcessedSource = kProcessed+        createCredentialsWithText = withText instanceId' \instanceId'' -> withText pipelineStep' \pipelineStep'' -> withText beamtime' \beamtime'' -> withText beamline' \beamline'' -> withText dataSource' \dataSource'' ->+          withText token' $+            asapo_create_source_credentials+              (convertSourceType sourceType)+              instanceId''+              pipelineStep''+              beamtime''+              beamline''+              dataSource''+    bracket createCredentialsWithText asapo_free_source_credentials f++peekCStringText :: CString -> IO Text+peekCStringText = (pack <$>) . peekCString++peekConstCStringText :: ConstPtr CChar -> IO Text+peekConstCStringText = (pack <$>) . peekCString . unConstPtr++withConstCString :: String -> (ConstCString -> IO b) -> IO b+withConstCString s f = withCString s (f . ConstPtr)++withConstText :: Text -> (ConstCString -> IO a) -> IO a+withConstText t = withConstCString (unpack t)++withCStringN :: Int -> (CString -> IO a) -> IO a+withCStringN size = bracket (mallocArray size) free++withCStringNToText :: Int -> (CString -> IO ()) -> IO Text+withCStringNToText size f =+  withCStringN size \ptr -> do+    f ptr+    pack <$> peekCString ptr++data StreamInfo = StreamInfo+  { streamInfoLastId :: MessageId,+    streamInfoName :: StreamName,+    streamInfoFinished :: Bool,+    streamInfoNextStream :: Text,+    streamInfoCreated :: UTCTime,+    streamInfoLastEntry :: UTCTime+  }+  deriving (Show)++-- Thanks to+--+-- https://github.com/imoverclocked/convert-times/blob/7f9b45bea8e62dbf14a156a8229b68e07efec5a1/app/Main.hs+timespecToUTC :: TimeSpec -> UTCTime+timespecToUTC sc_time =+  let scEpochInUTC :: UTCTime+      scEpochInUTC = zonedTimeToUTC $ fromJust $ RFC3339.parseTimeRFC3339 "1970-01-01T00:00:00.00Z"+      sc2diffTime = fromInteger (toNanoSecs sc_time) / 1000000000 :: NominalDiffTime+   in addUTCTime sc2diffTime scEpochInUTC++retrieveStreamInfoFromC :: AsapoStreamInfoHandle -> IO StreamInfo+retrieveStreamInfoFromC infoHandle = do+  lastId <- asapo_stream_info_get_last_id infoHandle+  name <- asapo_stream_info_get_name infoHandle >>= peekConstCStringText+  nextStream <- asapo_stream_info_get_next_stream infoHandle >>= peekConstCStringText+  finished <- asapo_stream_info_get_finished infoHandle+  created <- alloca \timespecPtr -> do+    asapo_stream_info_get_timestamp_created infoHandle timespecPtr+    timespec <- peek timespecPtr+    pure (timespecToUTC timespec)+  lastEntry <- alloca \timespecPtr -> do+    asapo_stream_info_get_timestamp_last_entry infoHandle timespecPtr+    timespec <- peek timespecPtr+    pure (timespecToUTC timespec)+  pure (StreamInfo (MessageId (fromIntegral lastId)) (StreamName name) (finished > 0) nextStream created lastEntry)++stringHandleToText :: AsapoStringHandle -> IO (Maybe Text)+stringHandleToText handle@(AsapoStringHandle handlePtr)+  | handlePtr == nullPtr = pure Nothing+  | otherwise = Just <$> (asapo_string_c_str handle >>= peekConstCStringText)++stringHandleToTextUnsafe :: AsapoStringHandle -> IO Text+stringHandleToTextUnsafe = asapo_string_c_str >=> peekConstCStringText++withPtr :: (Storable a) => a -> (Ptr a -> IO b) -> IO (a, b)+withPtr h f = with h \hPtr -> do+  result <- f hPtr+  first <- peek hPtr+  pure (first, result)
+ lib/Asapo/Either/Consumer.hs view
@@ -0,0 +1,620 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- |+-- Description : High-level interface for all consumer-related functions+--+-- To implement an ASAP:O consumer, you should only need this interface.+-- It exposes no memory-management functions (like free) or pointers, and+-- is thus safe to use.+module Asapo.Either.Consumer+  ( FilesystemFlag (..),+    ServerName (..),+    IncludeIncompleteFlag (..),+    SourcePath (..),+    Dataset (..),+    MessageMeta (..),+    DeleteFlag (..),+    ErrorOnNotExistFlag (..),+    Consumer,+    NetworkConnectionType (..),+    StreamFilter (..),+    MessageMetaHandle,+    GroupId,+    Error (..),+    ErrorType (..),+    withConsumer,+    retrieveDataForMessageMeta,+    queryMessages,+    resendNacs,+    getNextDataset,+    retrieveDataFromMeta,+    setStreamPersistent,+    getLastMessageMetaAndData,+    getLastMessageMeta,+    getLastMessageData,+    getLastInGroupMessageMetaAndData,+    getLastInGroupMessageMeta,+    getLastInGroupMessageData,+    getNextMessageMetaAndData,+    getNextMessageMeta,+    getNextMessageData,+    getCurrentDatasetCount,+    getBeamtimeMeta,+    getCurrentSize,+    acknowledge,+    negativeAcknowledge,+    getMessageMetaAndDataById,+    getMessageMetaById,+    getMessageDataById,+    getUnacknowledgedMessages,+    withGroupId,+    queryMessagesHandles,+    setTimeout,+    getLastDataset,+    getLastDatasetInGroup,+    resetLastReadMarker,+    setLastReadMarker,+    getCurrentConnectionType,+    getStreamList,+    deleteStream,+    resolveMetadata,+  )+where++import Asapo.Either.Common (MessageId (MessageId), SourceCredentials, StreamInfo, StreamName (StreamName), nominalDiffToMillis, peekConstCStringText, retrieveStreamInfoFromC, stringHandleToText, timespecToUTC, withCStringNToText, withConstText, withCredentials, withPtr)+import Asapo.Raw.Common (AsapoErrorHandle, AsapoMessageDataHandle (AsapoMessageDataHandle), AsapoStringHandle, ConstCString, asapo_error_explain, asapo_free_error_handle, asapo_free_stream_infos_handle, asapo_free_string_handle, asapo_is_error, asapo_new_error_handle, asapo_stream_infos_get_item)+import Asapo.Raw.Consumer (AsapoConsumerErrorType, AsapoConsumerHandle, AsapoDataSetHandle, AsapoIdListHandle, AsapoMessageMetaHandle (AsapoMessageMetaHandle), AsapoNetworkConnectionType, AsapoStreamFilter, asapo_consumer_acknowledge, asapo_consumer_current_connection_type, asapo_consumer_delete_stream, asapo_consumer_generate_new_group_id, asapo_consumer_get_beamtime_meta, asapo_consumer_get_by_id, asapo_consumer_get_current_dataset_count, asapo_consumer_get_current_size, asapo_consumer_get_last, asapo_consumer_get_last_dataset, asapo_consumer_get_last_dataset_ingroup, asapo_consumer_get_last_ingroup, asapo_consumer_get_next, asapo_consumer_get_next_dataset, asapo_consumer_get_stream_list, asapo_consumer_get_unacknowledged_messages, asapo_consumer_negative_acknowledge, asapo_consumer_query_messages, asapo_consumer_reset_last_read_marker, asapo_consumer_retrieve_data, asapo_consumer_set_last_read_marker, asapo_consumer_set_resend_nacs, asapo_consumer_set_stream_persistent, asapo_consumer_set_timeout, asapo_create_consumer, asapo_dataset_get_expected_size, asapo_dataset_get_id, asapo_dataset_get_item, asapo_dataset_get_size, asapo_error_get_type, asapo_free_consumer_handle, asapo_free_id_list_handle, asapo_free_message_metas_handle, asapo_id_list_get_item, asapo_id_list_get_size, asapo_message_data_get_as_chars, asapo_message_meta_get_buf_id, asapo_message_meta_get_dataset_substream, asapo_message_meta_get_id, asapo_message_meta_get_metadata, asapo_message_meta_get_name, asapo_message_meta_get_size, asapo_message_meta_get_source, asapo_message_meta_get_timestamp, asapo_message_metas_get_item, asapo_message_metas_get_size, asapo_stream_infos_get_size, kAllStreams, kAsapoTcp, kDataNotInCache, kEndOfStream, kFinishedStreams, kInterruptedTransaction, kLocalIOError, kNoData, kPartialData, kStreamFinished, kUnavailableService, kUndefined, kUnfinishedStreams, kUnsupportedClient, kWrongInput)+import Asapo.Raw.FreeHandleHack (p_asapo_free_handle)+import Control.Applicative (Applicative ((<*>)), pure)+import Control.Exception (bracket)+import Control.Monad (Monad ((>>=)), (>=>))+import Data.Bool (Bool, otherwise)+import qualified Data.ByteString as BS+import Data.Either (Either (Left, Right))+import Data.Eq (Eq ((==)))+import Data.Function (($), (.))+import Data.Functor ((<$>))+import Data.Int (Int)+import Data.Maybe (Maybe (Just, Nothing))+import Data.Ord ((>))+import Data.Text (Text)+import Data.Time (NominalDiffTime, UTCTime)+import Data.Traversable (Traversable (traverse))+import Data.Word (Word64)+import Foreign (Storable (peek), alloca)+import Foreign.C (CInt)+import Foreign.C.ConstPtr (ConstPtr (unConstPtr))+import Foreign.ForeignPtr (ForeignPtr, newForeignPtr, withForeignPtr)+import Foreign.Ptr (Ptr)+import System.IO (IO)+import Text.Show (Show)+import Prelude (Enum, Num ((-)), fromIntegral)++-- | Wrapper for a server name (something like "host:port")+newtype ServerName = ServerName Text++-- | Wrapper for a source path (dubious to not use @FilePath@, but let's see)+newtype SourcePath = SourcePath Text++-- | Whether to use the filesystem or do it in-memory+data FilesystemFlag = WithFilesystem | WithoutFilesystem deriving (Eq)++-- | Wrapper around a consumer handle. Create with the @withConsumer@ function(s).+newtype Consumer = Consumer AsapoConsumerHandle++data ErrorType+  = ErrorNoData+  | ErrorEndOfStream+  | ErrorStreamFinished+  | ErrorUnavailableService+  | ErrorInterruptedTransaction+  | ErrorLocalIOError+  | ErrorWrongInput+  | ErrorPartialData+  | ErrorUnsupportedClient+  | ErrorDataNotInCache+  | ErrorUnknownError+  deriving (Show)++convertErrorType :: AsapoConsumerErrorType -> ErrorType+convertErrorType x | x == kNoData = ErrorNoData+convertErrorType x | x == kEndOfStream = ErrorEndOfStream+convertErrorType x | x == kStreamFinished = ErrorStreamFinished+convertErrorType x | x == kUnavailableService = ErrorUnavailableService+convertErrorType x | x == kInterruptedTransaction = ErrorInterruptedTransaction+convertErrorType x | x == kLocalIOError = ErrorLocalIOError+convertErrorType x | x == kWrongInput = ErrorWrongInput+convertErrorType x | x == kPartialData = ErrorPartialData+convertErrorType x | x == kUnsupportedClient = ErrorUnsupportedClient+convertErrorType x | x == kDataNotInCache = ErrorDataNotInCache+convertErrorType _ = ErrorUnknownError++-- | Wrapper around an ASAP:O producer error, with an additional error code+data Error = Error+  { errorMessage :: Text,+    errorType :: ErrorType+  }+  deriving (Show)++checkErrorWithGivenHandle :: AsapoErrorHandle -> b -> IO (Either Error b)+checkErrorWithGivenHandle errorHandle result = do+  isError <- asapo_is_error errorHandle+  if isError > 0+    then do+      let explanationLength = 1024+      explanation <- withCStringNToText explanationLength \explanationPtr ->+        asapo_error_explain+          errorHandle+          explanationPtr+          (fromIntegral explanationLength)+      errorType' <- asapo_error_get_type errorHandle+      pure (Left (Error explanation (convertErrorType errorType')))+    else pure (Right result)++withErrorHandle :: (AsapoErrorHandle -> IO c) -> IO c+withErrorHandle = bracket asapo_new_error_handle asapo_free_error_handle++checkError :: (Ptr AsapoErrorHandle -> IO b) -> IO (Either Error b)+checkError f = do+  withErrorHandle \errorHandle -> do+    (errorHandlePtr, result) <- withPtr errorHandle f+    checkErrorWithGivenHandle errorHandlePtr result++withSuccess :: (Ptr AsapoErrorHandle -> IO t) -> (t -> IO (Either Error b)) -> IO (Either Error b)+withSuccess toCheck onSuccess = do+  result <- checkError toCheck+  case result of+    Left e -> pure (Left e)+    Right success -> onSuccess success++create :: ServerName -> SourcePath -> FilesystemFlag -> SourceCredentials -> IO (Either Error AsapoConsumerHandle)+create (ServerName serverName) (SourcePath sourcePath) fsFlag creds =+  withCredentials creds \creds' ->+    withConstText serverName \serverNameC ->+      withConstText sourcePath \sourcePathC ->+        checkError (asapo_create_consumer serverNameC sourcePathC (if fsFlag == WithFilesystem then 1 else 0) creds')++-- | Create a consumer and do something with it. This is the main entrypoint into the consumer+withConsumer :: forall a. ServerName -> SourcePath -> FilesystemFlag -> SourceCredentials -> (Error -> IO a) -> (Consumer -> IO a) -> IO a+withConsumer serverName sourcePath filesystemFlag creds onError onSuccess = bracket (create serverName sourcePath filesystemFlag creds) freeConsumer handle+  where+    freeConsumer :: Either Error AsapoConsumerHandle -> IO ()+    freeConsumer (Right h) = asapo_free_consumer_handle h+    freeConsumer _ = pure ()+    handle :: Either Error AsapoConsumerHandle -> IO a+    handle (Left e) = onError e+    handle (Right v) = onSuccess (Consumer v)++-- | Wrapper around a group ID+newtype GroupId = GroupId AsapoStringHandle++-- | Allocate a group ID and call a callback+withGroupId :: forall a. Consumer -> (Error -> IO a) -> (GroupId -> IO a) -> IO a+withGroupId (Consumer consumerHandle) onError onSuccess = bracket createGroupId destroy handle+  where+    createGroupId = withSuccess (asapo_consumer_generate_new_group_id consumerHandle) (pure . Right . GroupId)+    destroy (Right (GroupId stringHandle)) = asapo_free_string_handle stringHandle+    destroy _ = pure ()+    handle (Right v) = onSuccess v+    handle (Left e) = onError e++-- | Set the global consumer timeout+setTimeout :: Consumer -> NominalDiffTime -> IO ()+setTimeout (Consumer consumerHandle) timeout = asapo_consumer_set_timeout consumerHandle (nominalDiffToMillis timeout)++-- | Reset the last read marker for a specific group+resetLastReadMarker :: Consumer -> GroupId -> StreamName -> IO (Either Error Int)+resetLastReadMarker (Consumer consumer) (GroupId groupId) (StreamName streamName) =+  withConstText streamName \streamNameC -> (fromIntegral <$>) <$> checkError (asapo_consumer_reset_last_read_marker consumer groupId streamNameC)++-- | Set the last read marker for the stream+setLastReadMarker :: Consumer -> GroupId -> StreamName -> MessageId -> IO (Either Error Int)+setLastReadMarker (Consumer consumer) (GroupId groupId) (StreamName streamName) (MessageId value) =+  withConstText streamName \streamNameC -> (fromIntegral <$>) <$> checkError (asapo_consumer_set_last_read_marker consumer groupId value streamNameC)++-- | Acknowledge a specific message+acknowledge :: Consumer -> GroupId -> StreamName -> MessageId -> IO (Either Error Int)+acknowledge (Consumer consumer) (GroupId groupId) (StreamName streamName) (MessageId messageId) =+  withConstText streamName \streamNameC -> (fromIntegral <$>) <$> checkError (asapo_consumer_acknowledge consumer groupId messageId streamNameC)++-- | Negatively acknowledge a specific message+negativeAcknowledge ::+  Consumer ->+  GroupId ->+  StreamName ->+  MessageId ->+  -- | delay+  NominalDiffTime ->+  IO (Either Error Int)+negativeAcknowledge (Consumer consumer) (GroupId groupId) (StreamName streamName) (MessageId messageId) delay =+  withConstText streamName \streamNameC -> (fromIntegral <$>) <$> checkError (asapo_consumer_negative_acknowledge consumer groupId messageId (nominalDiffToMillis delay) streamNameC)++-- | Get a list of all unacknowledged message IDs in a range+getUnacknowledgedMessages :: Consumer -> GroupId -> StreamName -> (MessageId, MessageId) -> IO (Either Error [MessageId])+getUnacknowledgedMessages (Consumer consumer) (GroupId groupId) (StreamName streamName) (MessageId from, MessageId to) = bracket init destroy handle+  where+    init :: IO (Either Error AsapoIdListHandle)+    init = withConstText streamName $ checkError . asapo_consumer_get_unacknowledged_messages consumer groupId from to+    destroy :: Either Error AsapoIdListHandle -> IO ()+    destroy (Left _) = pure ()+    destroy (Right handle') = asapo_free_id_list_handle handle'+    handle :: Either Error AsapoIdListHandle -> IO (Either Error [MessageId])+    handle (Left e) = pure (Left e)+    handle (Right idListHandle) = do+      numberOfIds <- asapo_id_list_get_size idListHandle+      Right <$> repeatGetterWithSizeLimit ((MessageId <$>) . asapo_id_list_get_item idListHandle) numberOfIds++-- | Network connection type+data NetworkConnectionType+  = -- | not sure about this+    ConnectionUndefined+  | -- | TCP+    ConnectionTcp+  | -- | not sure about this+    ConnectionFabric++convertConnectionType :: AsapoNetworkConnectionType -> NetworkConnectionType+convertConnectionType x | x == kUndefined = ConnectionUndefined+convertConnectionType x | x == kAsapoTcp = ConnectionTcp+convertConnectionType _ = ConnectionFabric++-- | Retrieve the current consumer connection type+getCurrentConnectionType :: Consumer -> IO NetworkConnectionType+getCurrentConnectionType (Consumer consumerHandle) = convertConnectionType <$> asapo_consumer_current_connection_type consumerHandle++data StreamFilter = FilterAllStreams | FilterFinishedStreams | FilterUnfinishedStreams++convertStreamFilter :: StreamFilter -> AsapoStreamFilter+convertStreamFilter FilterAllStreams = kAllStreams+convertStreamFilter FilterFinishedStreams = kFinishedStreams+convertStreamFilter FilterUnfinishedStreams = kUnfinishedStreams++-- An often-repeating pattern: getting a length value and then either+-- returning an empty list or having another function to get the value+-- per ID. With an annoying off-by-one error.+repeatGetterWithSizeLimit :: (Eq a1, Num a1, Applicative f, Enum a1) => (a1 -> f a2) -> a1 -> f [a2]+repeatGetterWithSizeLimit f n+  | n == 0 = pure []+  | otherwise = traverse f [0 .. n - 1]++-- | Retrieve the list of streams with metadata+getStreamList ::+  Consumer ->+  -- | Pass @Nothing@ to get all streams+  Maybe StreamName ->+  StreamFilter ->+  IO (Either Error [StreamInfo])+getStreamList (Consumer consumer) streamName filter = bracket init destroy handle+  where+    init =+      let realStreamName = case streamName of+            Nothing -> ""+            Just (StreamName streamName') -> streamName'+       in withConstText realStreamName $ \streamNameC -> checkError (asapo_consumer_get_stream_list consumer streamNameC (convertStreamFilter filter))+    destroy (Left _) = pure ()+    destroy (Right handle') = asapo_free_stream_infos_handle handle'+    handle (Left e) = pure (Left e)+    handle (Right streamInfosHandle) = do+      numberOfStreams <- asapo_stream_infos_get_size streamInfosHandle+      Right+        <$> repeatGetterWithSizeLimit+          (asapo_stream_infos_get_item streamInfosHandle >=> retrieveStreamInfoFromC)+          numberOfStreams++-- | Anti-boolean-blindness for delete or not delete metadata when deleting a stream+data DeleteFlag = DeleteMeta | DontDeleteMeta deriving (Eq)++-- | Anti-boolean-blindness for "error on not existing data"+data ErrorOnNotExistFlag = ErrorOnNotExist | NoErrorOnNotExist deriving (Eq)++-- | Delete a given stream+deleteStream :: Consumer -> StreamName -> DeleteFlag -> ErrorOnNotExistFlag -> IO (Either Error Int)+deleteStream (Consumer consumer) (StreamName streamName) deleteFlag errorOnNotExistFlag =+  withConstText streamName \streamNameC ->+    (fromIntegral <$>)+      <$> checkError+        ( asapo_consumer_delete_stream+            consumer+            streamNameC+            (if deleteFlag == DeleteMeta then 1 else 0)+            (if errorOnNotExistFlag == ErrorOnNotExist then 1 else 0)+        )++-- | Set a stream persistent+setStreamPersistent :: Consumer -> StreamName -> IO (Either Error Int)+setStreamPersistent (Consumer consumer) (StreamName streamName) =+  withConstText streamName \streamNameC ->+    (fromIntegral <$>)+      <$> checkError+        ( asapo_consumer_set_stream_persistent+            consumer+            streamNameC+        )++-- | Get the current size (number of messages) of the stream+getCurrentSize :: Consumer -> StreamName -> IO (Either Error Int)+getCurrentSize (Consumer consumer) (StreamName streamName) =+  withConstText streamName \streamNameC ->+    (fromIntegral <$>)+      <$> checkError+        ( asapo_consumer_get_current_size+            consumer+            streamNameC+        )++-- | Anti-boolean-blindness for "include incomplete data sets in list"+data IncludeIncompleteFlag = IncludeIncomplete | ExcludeIncomplete deriving (Eq)++-- | Get number of datasets in stream+getCurrentDatasetCount :: Consumer -> StreamName -> IncludeIncompleteFlag -> IO (Either Error Int)+getCurrentDatasetCount (Consumer consumer) (StreamName streamName) inludeIncomplete =+  withConstText streamName \streamNameC ->+    (fromIntegral <$>)+      <$> checkError+        ( asapo_consumer_get_current_dataset_count+            consumer+            streamNameC+            (if inludeIncomplete == IncludeIncomplete then 1 else 0)+        )++-- | Get beamtime metadata (which can be not set, in which case @Nothing@ is returned)+getBeamtimeMeta :: Consumer -> IO (Either Error (Maybe Text))+getBeamtimeMeta (Consumer consumer) = checkError (asapo_consumer_get_beamtime_meta consumer) >>= traverse stringHandleToText++-- | Metadata handle, can be passed around as a pure value and be used to retrieve actual data for the metadata as a two-step process, using the @retrieveDataForMessageMeta@ function(s)+newtype MessageMetaHandle = MessageMetaHandle (ForeignPtr ()) deriving (Show)++withMessageMetaHandle :: MessageMetaHandle -> (AsapoMessageMetaHandle -> IO a) -> IO a+withMessageMetaHandle (MessageMetaHandle foreignPtr) f = withForeignPtr foreignPtr (f . AsapoMessageMetaHandle)++newMessageMetaHandle :: AsapoMessageMetaHandle -> IO MessageMetaHandle+newMessageMetaHandle (AsapoMessageMetaHandle inputPtr) = do+  MessageMetaHandle <$> newForeignPtr p_asapo_free_handle inputPtr++newtype MessageDataHandle = MessageDataHandle (ForeignPtr ()) deriving (Show)++withMessageDataHandle :: MessageDataHandle -> (AsapoMessageDataHandle -> IO a) -> IO a+withMessageDataHandle (MessageDataHandle foreignPtr) f = withForeignPtr foreignPtr (f . AsapoMessageDataHandle)++newMessageDataHandle :: AsapoMessageDataHandle -> IO MessageDataHandle+newMessageDataHandle (AsapoMessageDataHandle inputPtr) =+  MessageDataHandle <$> newForeignPtr p_asapo_free_handle inputPtr++wrapMessageMetaHandle :: AsapoMessageMetaHandle -> IO MessageMetaHandle+wrapMessageMetaHandle = newMessageMetaHandle++-- | Metadata for a single message+data MessageMeta = MessageMeta+  { messageMetaName :: Text,+    messageMetaTimestamp :: UTCTime,+    messageMetaSize :: Word64,+    messageMetaId :: MessageId,+    messageMetaSource :: Text,+    messageMetaMetadata :: Text,+    messageMetaBufId :: Word64,+    messageMetaDatasetSubstream :: Word64,+    messageMetaHandle :: MessageMetaHandle+  }+  deriving (Show)++-- | Retrieve actual data for the handle+retrieveDataForMessageMeta :: Consumer -> MessageMeta -> IO (Either Error BS.ByteString)+retrieveDataForMessageMeta consumer meta = retrieveDataFromMeta consumer (messageMetaHandle meta)++-- | Get the actual metadata hiding behind a handle (shouldn't be necessary when using the convenience interfaces)+resolveMetadata :: MessageMetaHandle -> IO MessageMeta+resolveMetadata metaHandle = withMessageMetaHandle metaHandle \meta -> do+  (timestamp, _) <- withPtr 0 (asapo_message_meta_get_timestamp meta)+  MessageMeta+    <$> (asapo_message_meta_get_name meta >>= peekConstCStringText)+    <*> pure (timespecToUTC timestamp)+    <*> asapo_message_meta_get_size meta+    <*> (MessageId <$> asapo_message_meta_get_id meta)+    <*> (asapo_message_meta_get_source meta >>= peekConstCStringText)+    <*> (asapo_message_meta_get_metadata meta >>= peekConstCStringText)+    <*> asapo_message_meta_get_buf_id meta+    <*> asapo_message_meta_get_dataset_substream meta+    <*> pure metaHandle++-- | Metadata for a dataset+data Dataset = Dataset+  { datasetId :: Word64,+    datasetExpectedSize :: Word64,+    datasetItems :: [MessageMetaHandle]+  }++retrieveDatasetFromC :: AsapoDataSetHandle -> IO Dataset+retrieveDatasetFromC handle = do+  numberOfItems <- asapo_dataset_get_size handle+  items <- repeatGetterWithSizeLimit (asapo_dataset_get_item handle >=> wrapMessageMetaHandle) numberOfItems+  Dataset <$> asapo_dataset_get_id handle <*> asapo_dataset_get_expected_size handle <*> pure items++-- | Get the next dataset for a stream+getNextDataset ::+  Consumer ->+  GroupId ->+  -- | Wait until dataset has these number of messages (0 for maximum size)+  Word64 ->+  StreamName ->+  IO (Either Error Dataset)+getNextDataset (Consumer consumer) (GroupId groupId) minSize (StreamName streamName) = withConstText streamName \streamNameC -> do+  checkError (asapo_consumer_get_next_dataset consumer groupId minSize streamNameC) >>= traverse retrieveDatasetFromC++-- | Get the last dataset in the stream+getLastDataset ::+  Consumer ->+  -- | Wait until dataset has these number of messages (0 for maximum size)+  Word64 ->+  StreamName ->+  IO (Either Error Dataset)+getLastDataset (Consumer consumer) minSize (StreamName streamName) = withConstText streamName \streamNameC -> do+  checkError (asapo_consumer_get_last_dataset consumer minSize streamNameC) >>= traverse retrieveDatasetFromC++-- | Get the last data ste in the given group+getLastDatasetInGroup ::+  Consumer ->+  GroupId ->+  -- | Wait until dataset has these number of messages (0 for maximum size)+  Word64 ->+  StreamName ->+  IO (Either Error Dataset)+getLastDatasetInGroup (Consumer consumer) (GroupId groupId) minSize (StreamName streamName) = withConstText streamName \streamNameC -> do+  checkError (asapo_consumer_get_last_dataset_ingroup consumer groupId minSize streamNameC) >>= traverse retrieveDatasetFromC++retrieveDataFromHandle :: MessageDataHandle -> IO BS.ByteString+retrieveDataFromHandle dataHandle = do+  withMessageDataHandle dataHandle \dataHandlePtr -> do+    messageCString <- asapo_message_data_get_as_chars dataHandlePtr+    BS.packCString (unConstPtr messageCString)++-- | Retrieve data for the given metadata handle+retrieveDataFromMeta :: Consumer -> MessageMetaHandle -> IO (Either Error BS.ByteString)+retrieveDataFromMeta (Consumer consumer) metaHandle =+  withMessageMetaHandle metaHandle \metaHandlePtr ->+    alloca \dataHandlePtrPtr ->+      withSuccess (asapo_consumer_retrieve_data consumer metaHandlePtr dataHandlePtrPtr) \_result -> do+        dataHandlePtr <- peek dataHandlePtrPtr+        dataHandle <- newMessageDataHandle dataHandlePtr+        Right <$> retrieveDataFromHandle dataHandle++withMessageHandles ::+  StreamName ->+  (Ptr AsapoMessageMetaHandle -> Ptr AsapoMessageDataHandle -> ConstCString -> Ptr AsapoErrorHandle -> IO CInt) ->+  (MessageMetaHandle -> MessageDataHandle -> IO (Either Error a)) ->+  IO (Either Error a)+withMessageHandles (StreamName streamName) g f = do+  alloca \metaHandlePtrPtr ->+    alloca \dataHandlePtrPtr ->+      withConstText streamName \streamNameC ->+        withSuccess (g metaHandlePtrPtr dataHandlePtrPtr streamNameC) \_result -> do+          metaHandlePtr <- peek metaHandlePtrPtr+          dataHandlePtr <- peek dataHandlePtrPtr+          metaHandle <- newMessageMetaHandle metaHandlePtr+          dataHandle <- newMessageDataHandle dataHandlePtr+          f metaHandle dataHandle++withMessageHandlesById :: Consumer -> StreamName -> MessageId -> (MessageMetaHandle -> MessageDataHandle -> IO (Either Error a)) -> IO (Either Error a)+withMessageHandlesById (Consumer consumer) streamName (MessageId messageId) =+  withMessageHandles+    streamName+    (asapo_consumer_get_by_id consumer messageId)++retrieveMessageMetaAndData :: MessageMetaHandle -> MessageDataHandle -> IO (Either a (MessageMeta, BS.ByteString))+retrieveMessageMetaAndData metaHandle dataHandle = do+  data' <- retrieveDataFromHandle dataHandle+  meta <- resolveMetadata metaHandle+  pure (Right (meta, data'))++retrieveMessageMeta :: MessageMetaHandle -> p -> IO (Either a MessageMeta)+retrieveMessageMeta metaHandle _dataHandle = do+  meta <- resolveMetadata metaHandle+  pure (Right meta)++retrieveMessageData :: p -> MessageDataHandle -> IO (Either a BS.ByteString)+retrieveMessageData _metaHandle dataHandle = do+  data' <- retrieveDataFromHandle dataHandle+  pure (Right data')++-- | Given a message ID, retrieve both metadata and data+getMessageMetaAndDataById :: Consumer -> StreamName -> MessageId -> IO (Either Error (MessageMeta, BS.ByteString))+getMessageMetaAndDataById consumer streamName messageId =+  withMessageHandlesById consumer streamName messageId retrieveMessageMetaAndData++-- | Given a message ID, retrieve only the metadata (you can get the data later with 'retrieveDataFromMessageMeta')+getMessageMetaById :: Consumer -> StreamName -> MessageId -> IO (Either Error MessageMeta)+getMessageMetaById consumer streamName messageId = do+  withMessageHandlesById consumer streamName messageId retrieveMessageMeta++-- | Given a message ID, retrieve only the data+getMessageDataById :: Consumer -> StreamName -> MessageId -> IO (Either Error BS.ByteString)+getMessageDataById consumer streamName messageId = do+  withMessageHandlesById consumer streamName messageId retrieveMessageData++-- | Retrieve the last message in the stream, with data and metadata+getLastMessageMetaAndData :: Consumer -> StreamName -> IO (Either Error (MessageMeta, BS.ByteString))+getLastMessageMetaAndData (Consumer consumer) streamName = withMessageHandles streamName (asapo_consumer_get_last consumer) retrieveMessageMetaAndData++-- | Retrieve the last message in the stream, only metadata (you can get the data later with 'retrieveDataFromMessageMeta')+getLastMessageMeta :: Consumer -> StreamName -> IO (Either Error MessageMeta)+getLastMessageMeta (Consumer consumer) streamName = withMessageHandles streamName (asapo_consumer_get_last consumer) retrieveMessageMeta++-- | Retrieve the last message in the stream, only data+getLastMessageData :: Consumer -> StreamName -> IO (Either Error BS.ByteString)+getLastMessageData (Consumer consumer) streamName = withMessageHandles streamName (asapo_consumer_get_last consumer) retrieveMessageData++-- | Retrieve the last message in a given stream and group, with data and metadata+getLastInGroupMessageMetaAndData :: Consumer -> StreamName -> GroupId -> IO (Either Error (MessageMeta, BS.ByteString))+getLastInGroupMessageMetaAndData (Consumer consumer) streamName (GroupId groupId) = withMessageHandles streamName (asapo_consumer_get_last_ingroup consumer groupId) retrieveMessageMetaAndData++-- | Retrieve the last message in a given stream and group, only metadata (you can get the data later with 'retrieveDataFromMessageMeta')+getLastInGroupMessageMeta :: Consumer -> StreamName -> GroupId -> IO (Either Error MessageMeta)+getLastInGroupMessageMeta (Consumer consumer) streamName (GroupId groupId) = withMessageHandles streamName (asapo_consumer_get_last_ingroup consumer groupId) retrieveMessageMeta++-- | Retrieve the last message in a given stream and group, only data+getLastInGroupMessageData :: Consumer -> StreamName -> GroupId -> IO (Either Error BS.ByteString)+getLastInGroupMessageData (Consumer consumer) streamName (GroupId groupId) = withMessageHandles streamName (asapo_consumer_get_last_ingroup consumer groupId) retrieveMessageData++-- | Retrieve the next message in the stream and group, with data and metadata+getNextMessageMetaAndData :: Consumer -> StreamName -> GroupId -> IO (Either Error (MessageMeta, BS.ByteString))+getNextMessageMetaAndData (Consumer consumer) streamName (GroupId groupId) =+  withMessageHandles+    streamName+    (asapo_consumer_get_next consumer groupId)+    retrieveMessageMetaAndData++-- | Retrieve the next message in the stream and group, only metadata (you can get the data later with 'retrieveDataFromMessageMeta')+getNextMessageMeta :: Consumer -> StreamName -> GroupId -> IO (Either Error MessageMeta)+getNextMessageMeta (Consumer consumer) streamName (GroupId groupId) = withMessageHandles streamName (asapo_consumer_get_next consumer groupId) retrieveMessageMeta++-- | Retrieve the next message in the stream and group, only data+getNextMessageData :: Consumer -> StreamName -> GroupId -> IO (Either Error BS.ByteString)+getNextMessageData (Consumer consumer) streamName (GroupId groupId) = withMessageHandles streamName (asapo_consumer_get_next consumer groupId) retrieveMessageData++-- | Query messages, return handles without data+queryMessagesHandles ::+  Consumer ->+  -- | Actual query string, see the docs for syntax+  Text ->+  StreamName ->+  IO (Either Error [MessageMetaHandle])+queryMessagesHandles (Consumer consumer) query (StreamName streamName) = withConstText streamName \streamNameC -> withConstText query \queryC ->+  let init = checkError (asapo_consumer_query_messages consumer queryC streamNameC)+      destroy (Left _) = pure ()+      destroy (Right v) = asapo_free_message_metas_handle v+   in bracket init destroy \case+        Left e -> pure (Left e)+        Right metasHandle' -> do+          numberOfMetas <- asapo_message_metas_get_size metasHandle'+          Right <$> repeatGetterWithSizeLimit (asapo_message_metas_get_item metasHandle' >=> wrapMessageMetaHandle) numberOfMetas++-- | Query messages, return handles without data+queryMessages ::+  Consumer ->+  -- | Actual query string, see the docs for syntax+  Text ->+  StreamName ->+  IO (Either Error [MessageMeta])+queryMessages (Consumer consumer) query (StreamName streamName) = withConstText streamName \streamNameC -> withConstText query \queryC ->+  let init = checkError (asapo_consumer_query_messages consumer queryC streamNameC)+      destroy (Left _) = pure ()+      destroy (Right v) = asapo_free_message_metas_handle v+   in bracket init destroy \case+        Left e -> pure (Left e)+        Right metasHandle' -> do+          numberOfMetas <- asapo_message_metas_get_size metasHandle'+          Right <$> repeatGetterWithSizeLimit (asapo_message_metas_get_item metasHandle' >=> wrapMessageMetaHandle >=> resolveMetadata) numberOfMetas++-- | Reset negative acknowledgements+resendNacs ::+  Consumer ->+  -- | resend yes/no+  Bool ->+  -- | delay+  NominalDiffTime ->+  -- | attempts+  Word64 ->+  IO ()+resendNacs (Consumer consumer) resend delay = asapo_consumer_set_resend_nacs consumer (if resend then 1 else 0) (nominalDiffToMillis delay)
+ lib/Asapo/Either/Producer.hs view
@@ -0,0 +1,688 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- |+-- Description : High-level interface for all producer-related functions+--+-- To implement an ASAP:O producer, you should only need this interface.+-- It exposes no memory-management functions (like free) or pointers, and+-- is thus safe to use.+module Asapo.Either.Producer+  ( Endpoint (..),+    ProcessingThreads (..),+    RequestHandlerType (..),+    Error (..),+    Metadata (..),+    SourceCredentials,+    DeletionFlags (..),+    Producer,+    LogLevel (..),+    FileName (..),+    DatasetSubstream (..),+    DatasetSize (..),+    VersionInfo (..),+    UpsertMode (..),+    MetadataIngestMode (..),+    AutoIdFlag (..),+    TransferFlag (..),+    StorageFlag (..),+    RequestResponse (..),+    Opcode (..),+    GenericRequestHeader (..),+    getRequestsQueueSize,+    getRequestsQueueVolumeMb,+    setRequestsQueueLimits,+    checkError,+    checkErrorWithGivenHandle,+    withProducer,+    enableLocalLog,+    waitRequestsFinished,+    getVersionInfo,+    getStreamInfo,+    getStreamMeta,+    getBeamtimeMeta,+    deleteStream,+    getLastStream,+    send,+    sendFile,+    sendStreamFinishedFlag,+    sendBeamtimeMetadata,+    sendStreamMetadata,+    setLogLevel,+    enableRemoteLog,+    setCredentials,+  )+where++import Asapo.Either.Common (MessageId (MessageId), SourceCredentials, StreamInfo, StreamName (StreamName), nominalDiffToMillis, peekCStringText, retrieveStreamInfoFromC, stringHandleToText, stringHandleToTextUnsafe, withCStringNToText, withConstText, withCredentials, withPtr, withText)+import Asapo.Raw.Common (AsapoErrorHandle, AsapoStreamInfoHandle, AsapoStringHandle, asapo_error_explain, asapo_free_error_handle, asapo_free_stream_info_handle, asapo_free_string_handle, asapo_is_error, asapo_new_error_handle, asapo_new_string_handle)+import Asapo.Raw.Producer+  ( AsapoGenericRequestHeader (AsapoGenericRequestHeader),+    AsapoLogLevel,+    AsapoMessageHeaderHandle,+    AsapoOpcode,+    AsapoProducerHandle,+    AsapoRequestCallbackPayloadHandle,+    asapoLogLevelDebug,+    asapoLogLevelError,+    asapoLogLevelInfo,+    asapoLogLevelNone,+    asapoLogLevelWarning,+    asapo_create_message_header,+    asapo_create_producer,+    asapo_free_message_header_handle,+    asapo_free_producer_handle,+    asapo_producer_delete_stream,+    asapo_producer_enable_local_log,+    asapo_producer_enable_remote_log,+    asapo_producer_get_beamtime_meta,+    asapo_producer_get_last_stream,+    asapo_producer_get_requests_queue_size,+    asapo_producer_get_requests_queue_volume_mb,+    asapo_producer_get_stream_info,+    asapo_producer_get_stream_meta,+    asapo_producer_get_version_info,+    asapo_producer_send,+    asapo_producer_send_beamtime_metadata,+    asapo_producer_send_file,+    asapo_producer_send_stream_finished_flag,+    asapo_producer_send_stream_metadata,+    asapo_producer_set_credentials,+    asapo_producer_set_log_level,+    asapo_producer_set_requests_queue_limits,+    asapo_producer_wait_requests_finished,+    asapo_request_callback_payload_get_original_header,+    asapo_request_callback_payload_get_response,+    createRequestCallback,+    kFilesystem,+    kInsert,+    kOpcodeAuthorize,+    kOpcodeCount,+    kOpcodeDeleteStream,+    kOpcodeGetBufferData,+    kOpcodeGetMeta,+    kOpcodeLastStream,+    kOpcodeStreamInfo,+    kOpcodeTransferData,+    kOpcodeTransferDatasetData,+    kOpcodeTransferMetaData,+    kOpcodeUnknownOp,+    kReplace,+    kStoreInDatabase,+    kStoreInFilesystem,+    kTcp,+    kTransferData,+    kTransferMetaDataOnly,+    kUpdate,+  )+import Control.Applicative (Applicative (pure))+import Control.Exception (bracket)+import Data.Bits ((.|.))+import Data.Bool (Bool)+import qualified Data.ByteString as BS+import Data.ByteString.Unsafe (unsafeUseAsCString)+import Data.Either (Either (Left, Right))+import Data.Eq (Eq ((==)))+import Data.Foldable (Foldable (elem))+import Data.Functor ((<$>))+import Data.Int (Int)+import Data.Maybe (Maybe (Just, Nothing))+import Data.Ord ((>))+import Data.Text (Text)+import Data.Time (NominalDiffTime)+import Data.Word (Word64)+import Foreign (Storable (peek), alloca, castPtr)+import Foreign.C.ConstPtr (ConstPtr (unConstPtr))+import Foreign.Ptr (Ptr)+import System.IO (IO)+import Text.Show (Show)+import Prelude (fromIntegral)++-- | Wrapper around an ASAP:O producer error. Note that there is only+--  an error "explanation" here, no error code, since the C interface+--  does not expose this.+newtype Error = Error Text deriving (Show)++-- | Wrapper around an ASAP:O producer endpoint (usually something like "host:port")+newtype Endpoint = Endpoint Text++-- | Wrapper around the number of ASAP:O processing threads (simply to make call signatures mor readable)+newtype ProcessingThreads = ProcessingThreads Int++-- | This has no documentation in ASAP:O yet+data RequestHandlerType = TcpHandler | FilesystemHandler++-- | Opaque wrapper around an ASAP:O producer+newtype Producer = Producer AsapoProducerHandle++-- | Internal function to check and return either an error (if it's+-- present behind the given handle) or a "result" of some function+-- that produced the error handle+checkErrorWithGivenHandle :: AsapoErrorHandle -> b -> IO (Either Error b)+checkErrorWithGivenHandle errorHandle result = do+  isError <- asapo_is_error errorHandle+  if isError > 0+    then do+      let explanationLength = 1024+      explanation <- withCStringNToText explanationLength \explanationPtr ->+        asapo_error_explain+          errorHandle+          explanationPtr+          (fromIntegral explanationLength)+      pure (Left (Error explanation))+    else pure (Right result)++withErrorHandle :: (AsapoErrorHandle -> IO c) -> IO c+withErrorHandle = bracket asapo_new_error_handle asapo_free_error_handle++-- | Helper function since most ASAP:O functions receive a pointer to+-- an error handle as the last argument, so error checking becomes+-- "abstractable"+checkError :: (Ptr AsapoErrorHandle -> IO b) -> IO (Either Error b)+checkError f = do+  withErrorHandle \errorHandle -> do+    (errorHandlePtr, result) <- withPtr errorHandle f+    checkErrorWithGivenHandle errorHandlePtr result++create :: Endpoint -> ProcessingThreads -> RequestHandlerType -> SourceCredentials -> NominalDiffTime -> IO (Either Error AsapoProducerHandle)+create (Endpoint endpoint) (ProcessingThreads processingThreads) handlerType sourceCredentials timeout = do+  withCredentials sourceCredentials \credentials' ->+    let convertHandlerType TcpHandler = kTcp+        convertHandlerType FilesystemHandler = kFilesystem+     in do+          withText endpoint \endpoint' -> do+            checkError+              ( asapo_create_producer+                  endpoint'+                  (fromIntegral processingThreads)+                  (convertHandlerType handlerType)+                  credentials'+                  (nominalDiffToMillis timeout)+              )++-- | Create a producer and do something with it. This is the main entrypoint into the producer+withProducer ::+  forall a.+  Endpoint ->+  ProcessingThreads ->+  RequestHandlerType ->+  SourceCredentials ->+  -- | timeout+  NominalDiffTime ->+  (Error -> IO a) ->+  (Producer -> IO a) ->+  IO a+withProducer endpoint processingThreads handlerType sourceCredentials timeout onError onSuccess = bracket (create endpoint processingThreads handlerType sourceCredentials timeout) freeProducer handle+  where+    freeProducer :: Either Error AsapoProducerHandle -> IO ()+    freeProducer (Left _) = pure ()+    freeProducer (Right producerHandle) = asapo_free_producer_handle producerHandle+    handle :: Either Error AsapoProducerHandle -> IO a+    handle (Left e) = onError e+    handle (Right v) = onSuccess (Producer v)++withStringHandle :: (AsapoStringHandle -> IO c) -> IO c+withStringHandle = bracket asapo_new_string_handle asapo_free_string_handle++data VersionInfo = VersionInfo+  { versionClient :: Text,+    versionServer :: Text,+    versionSupported :: Bool+  }+  deriving (Show)++-- | Retrieve producer version info+getVersionInfo :: Producer -> IO (Either Error VersionInfo)+getVersionInfo (Producer producerHandle) =+  withStringHandle \clientInfo -> withStringHandle \serverInfo -> alloca \supportedPtr -> do+    result <- checkError (asapo_producer_get_version_info producerHandle clientInfo serverInfo supportedPtr)+    case result of+      Left e -> pure (Left e)+      -- The return value is a CInt which is unnecessary probably?+      Right _integerReturnCode -> do+        supported <- peek supportedPtr+        clientInfo' <- stringHandleToTextUnsafe clientInfo+        serverInfo' <- stringHandleToTextUnsafe serverInfo+        pure (Right (VersionInfo clientInfo' serverInfo' (supported > 0)))++-- | Retrieve info for a single stream+getStreamInfo ::+  Producer ->+  StreamName ->+  -- | Timeout+  NominalDiffTime ->+  IO (Either Error StreamInfo)+getStreamInfo (Producer producer) (StreamName stream) timeout = bracket init destroy f+  where+    init :: IO (Either Error AsapoStreamInfoHandle)+    init = withConstText stream \streamC -> checkError (asapo_producer_get_stream_info producer streamC (nominalDiffToMillis timeout))+    destroy :: Either Error AsapoStreamInfoHandle -> IO ()+    destroy (Right handle) = asapo_free_stream_info_handle handle+    destroy _ = pure ()+    f :: Either Error AsapoStreamInfoHandle -> IO (Either Error StreamInfo)+    f (Left e) = pure (Left e)+    f (Right streamInfoHandle) = Right <$> retrieveStreamInfoFromC streamInfoHandle++-- | Retrieve info for the latest stream+getLastStream ::+  Producer ->+  -- | Timeout+  NominalDiffTime ->+  IO (Either Error StreamInfo)+getLastStream (Producer producer) timeout = bracket init destroy f+  where+    init :: IO (Either Error AsapoStreamInfoHandle)+    init = checkError (asapo_producer_get_last_stream producer (nominalDiffToMillis timeout))+    destroy :: Either Error AsapoStreamInfoHandle -> IO ()+    destroy (Right handle) = asapo_free_stream_info_handle handle+    destroy _ = pure ()+    f :: Either Error AsapoStreamInfoHandle -> IO (Either Error StreamInfo)+    f (Left e) = pure (Left e)+    f (Right streamInfoHandle) = Right <$> retrieveStreamInfoFromC streamInfoHandle++-- | Retrieve metadata for the given stream (which might be missing, in which case @Nothing@ is returned)+getStreamMeta ::+  Producer ->+  StreamName ->+  -- | timeout+  NominalDiffTime ->+  IO (Either Error (Maybe Text))+getStreamMeta (Producer producer) (StreamName stream) timeout = bracket init destroy f+  where+    init :: IO (Either Error AsapoStringHandle)+    init = withConstText stream \streamC -> checkError (asapo_producer_get_stream_meta producer streamC (nominalDiffToMillis timeout))+    destroy :: Either Error AsapoStringHandle -> IO ()+    destroy (Right handle) = asapo_free_string_handle handle+    destroy _ = pure ()+    f :: Either Error AsapoStringHandle -> IO (Either Error (Maybe Text))+    f (Left e) = pure (Left e)+    f (Right string) = Right <$> stringHandleToText string++-- | Retrieve metadata for the given stream (which might be missing, in which case @Nothing@ is returned)+getBeamtimeMeta ::+  Producer ->+  -- | timeout+  NominalDiffTime ->+  IO (Either Error (Maybe Text))+getBeamtimeMeta (Producer producer) timeout = bracket init destroy f+  where+    init :: IO (Either Error AsapoStringHandle)+    init = checkError (asapo_producer_get_beamtime_meta producer (nominalDiffToMillis timeout))+    destroy :: Either Error AsapoStringHandle -> IO ()+    destroy (Right handle) = asapo_free_string_handle handle+    destroy _ = pure ()+    f :: Either Error AsapoStringHandle -> IO (Either Error (Maybe Text))+    f (Left e) = pure (Left e)+    f (Right string) = Right <$> stringHandleToText string++data DeletionFlags+  = -- | Delete metadata also+    DeleteMeta+  | -- | Don't throw an error if the data doesn't exist anyways+    DeleteErrorOnNotExist+  deriving (Eq)++-- | Delete the given stream+deleteStream ::+  Producer ->+  StreamName ->+  -- | timeout+  NominalDiffTime ->+  [DeletionFlags] ->+  IO (Either Error Int)+deleteStream (Producer producer) (StreamName stream) timeout deletionFlags = do+  result <- withConstText stream \streamC ->+    checkError+      ( asapo_producer_delete_stream+          producer+          streamC+          (nominalDiffToMillis timeout)+          (if DeleteMeta `elem` deletionFlags then 1 else 0)+          (if DeleteErrorOnNotExist `elem` deletionFlags then 1 else 0)+      )+  pure (fromIntegral <$> result)++-- | Wrapper around file name (dubious to use @Text@ here, but fine for now)+newtype FileName = FileName Text++-- | Wrapper around metadata to be produced+newtype Metadata = Metadata Text++-- | Wrapper around the substream to use+newtype DatasetSubstream = DatasetSubstream Int++-- | Wrapper around the dataset size to use+newtype DatasetSize = DatasetSize Int++-- | Anti-boolean-blindness for the "auto id" flag in the message header+data AutoIdFlag = UseAutoId | NoAutoId deriving (Eq)++-- | Which data to transfer+data TransferFlag = DataAndMetadata | MetadataOnly++-- | Where to store the data+data StorageFlag = Filesystem | Database | FilesystemAndDatabase++convertSendFlags :: TransferFlag -> StorageFlag -> Word64+convertSendFlags tf sf = convertTransferFlag tf .|. convertStorageFlag sf+  where+    convertTransferFlag :: TransferFlag -> Word64+    convertTransferFlag DataAndMetadata = fromIntegral kTransferData+    convertTransferFlag MetadataOnly = fromIntegral kTransferMetaDataOnly+    convertStorageFlag :: StorageFlag -> Word64+    convertStorageFlag Filesystem = fromIntegral kStoreInFilesystem+    convertStorageFlag Database = fromIntegral kStoreInDatabase+    convertStorageFlag FilesystemAndDatabase = fromIntegral kStoreInDatabase .|. fromIntegral kStoreInFilesystem++-- | Internal function to create a message header handle, which gets used to send data.+withMessageHeaderHandle ::+  MessageId ->+  FileName ->+  Metadata ->+  DatasetSubstream ->+  DatasetSize ->+  AutoIdFlag ->+  Int ->+  (AsapoMessageHeaderHandle -> IO b) ->+  IO b+withMessageHeaderHandle+  (MessageId messageId)+  (FileName fileName)+  (Metadata metadata)+  (DatasetSubstream datasetSubstream)+  (DatasetSize datasetSize)+  autoIdFlag+  dataSize = bracket init destroy+    where+      init :: IO AsapoMessageHeaderHandle+      init = withConstText fileName \fileNameC -> withConstText metadata \metadataC ->+        asapo_create_message_header+          messageId+          (fromIntegral dataSize)+          fileNameC+          metadataC+          (fromIntegral datasetSubstream)+          (fromIntegral datasetSize)+          (if autoIdFlag == UseAutoId then 1 else 0)+      destroy = asapo_free_message_header_handle++data Opcode+  = OpcodeUnknownOp+  | OpcodeTransferData+  | OpcodeTransferDatasetData+  | OpcodeStreamInfo+  | OpcodeLastStream+  | OpcodeGetBufferData+  | OpcodeAuthorize+  | OpcodeTransferMetaData+  | OpcodeDeleteStream+  | OpcodeGetMeta+  | OpcodeCount+  | OpcodePersistStream++convertOpcode :: AsapoOpcode -> Opcode+convertOpcode x | x == kOpcodeUnknownOp = OpcodeUnknownOp+convertOpcode x | x == kOpcodeTransferData = OpcodeTransferData+convertOpcode x | x == kOpcodeTransferDatasetData = OpcodeTransferDatasetData+convertOpcode x | x == kOpcodeStreamInfo = OpcodeStreamInfo+convertOpcode x | x == kOpcodeLastStream = OpcodeLastStream+convertOpcode x | x == kOpcodeGetBufferData = OpcodeGetBufferData+convertOpcode x | x == kOpcodeAuthorize = OpcodeAuthorize+convertOpcode x | x == kOpcodeTransferMetaData = OpcodeTransferMetaData+convertOpcode x | x == kOpcodeDeleteStream = OpcodeDeleteStream+convertOpcode x | x == kOpcodeGetMeta = OpcodeGetMeta+convertOpcode x | x == kOpcodeCount = OpcodeCount+convertOpcode _ = OpcodePersistStream++-- | Information about the send request, to be used in the ASAP:O send callback+data GenericRequestHeader = GenericRequestHeader+  { genericRequestHeaderOpCode :: Opcode,+    genericRequestHeaderDataId :: Int,+    genericRequestHeaderDataSize :: Int,+    genericRequestHeaderMetaSize :: Int,+    genericRequestHeaderCustomData :: [Int],+    genericRequestHeaderMessage :: BS.ByteString,+    genericRequestHeaderStream :: Text,+    genericRequestHeaderApiVersion :: Text+  }++convertRequestHeader :: AsapoGenericRequestHeader -> IO GenericRequestHeader+convertRequestHeader (AsapoGenericRequestHeader opcode dataId dataSize metaSize customData message stream apiVersion) = do+  streamText <- peekCStringText stream+  apiVersionText <- peekCStringText apiVersion+  messageAsBs <- BS.packCStringLen (message, fromIntegral dataSize)+  let customData' :: [Int]+      customData' = fromIntegral <$> customData+  pure+    ( GenericRequestHeader+        (convertOpcode opcode)+        (fromIntegral dataId)+        (fromIntegral dataSize)+        (fromIntegral metaSize)+        customData'+        messageAsBs+        streamText+        apiVersionText+    )++-- | Information about the request and its response, to be used in the ASAP:O send callback+data RequestResponse = RequestResponse+  { responsePayload :: Text,+    responseOriginalRequestHeader :: GenericRequestHeader,+    responseError :: Maybe Error+  }++sendRequestCallback :: (RequestResponse -> IO ()) -> Ptr () -> AsapoRequestCallbackPayloadHandle -> AsapoErrorHandle -> IO ()+sendRequestCallback simpleCallback _data payloadHandle errorHandle = do+  payloadText <- bracket (asapo_request_callback_payload_get_response payloadHandle) asapo_free_string_handle stringHandleToTextUnsafe+  originalHeaderCPtr <- asapo_request_callback_payload_get_original_header payloadHandle+  originalHeaderC <- peek (unConstPtr originalHeaderCPtr)+  originalHeader <- convertRequestHeader originalHeaderC+  errorHandle' <- checkErrorWithGivenHandle errorHandle ()+  case errorHandle' of+    Left e -> simpleCallback (RequestResponse payloadText originalHeader (Just e))+    _ -> simpleCallback (RequestResponse payloadText originalHeader Nothing)++-- | Send a message containing raw data. Due to newtype and enum usage, all parameter should be self-explanatory+send ::+  Producer ->+  MessageId ->+  FileName ->+  Metadata ->+  DatasetSubstream ->+  DatasetSize ->+  AutoIdFlag ->+  BS.ByteString ->+  TransferFlag ->+  StorageFlag ->+  StreamName ->+  (RequestResponse -> IO ()) ->+  IO (Either Error Int)+send (Producer producer) messageId fileName metadata datasetSubstream datasetSize autoIdFlag data' transferFlag storageFlag (StreamName stream) callback =+  withMessageHeaderHandle+    messageId+    fileName+    metadata+    datasetSubstream+    datasetSize+    autoIdFlag+    (BS.length data')+    \messageHeaderHandle ->+      unsafeUseAsCString data' \data'' -> withConstText stream \streamC -> do+        requestCallback <- createRequestCallback (sendRequestCallback callback)+        ( fromIntegral+            <$>+          )+          <$> checkError+            ( asapo_producer_send+                producer+                messageHeaderHandle+                (castPtr data'')+                (convertSendFlags transferFlag storageFlag)+                streamC+                requestCallback+            )++-- | Send a message containing a file. Due to newtype and enum usage, all parameter should be self-explanatory+sendFile ::+  Producer ->+  MessageId ->+  -- | File name to put into the message header+  FileName ->+  Metadata ->+  DatasetSubstream ->+  DatasetSize ->+  AutoIdFlag ->+  -- | Size+  Int ->+  -- | File to actually send+  FileName ->+  TransferFlag ->+  StorageFlag ->+  StreamName ->+  (RequestResponse -> IO ()) ->+  IO (Either Error Int)+sendFile (Producer producer) messageId fileName meta datasetSubstream datasetSize autoIdFlag size (FileName fileNameToSend) transferFlag storageFlag (StreamName stream) callback =+  withMessageHeaderHandle+    messageId+    fileName+    meta+    datasetSubstream+    datasetSize+    autoIdFlag+    size+    \messageHeaderHandle ->+      withConstText fileNameToSend \fileNameToSendC -> withConstText stream \streamC -> do+        requestCallback <- createRequestCallback (sendRequestCallback callback)+        ( fromIntegral+            <$>+          )+          <$> checkError+            ( asapo_producer_send_file+                producer+                messageHeaderHandle+                fileNameToSendC+                (convertSendFlags transferFlag storageFlag)+                streamC+                requestCallback+            )++-- | As the title says, send the "stream finished" flag+sendStreamFinishedFlag :: Producer -> StreamName -> MessageId -> StreamName -> (RequestResponse -> IO ()) -> IO (Either Error Int)+sendStreamFinishedFlag (Producer producer) (StreamName stream) (MessageId lastId) (StreamName nextStream) callback = do+  requestCallback <- createRequestCallback (sendRequestCallback callback)+  withConstText stream \streamC -> withConstText nextStream \nextStreamC ->+    (fromIntegral <$>)+      <$> checkError+        ( asapo_producer_send_stream_finished_flag+            producer+            streamC+            lastId+            nextStreamC+            requestCallback+        )++data MetadataIngestMode = Insert | Replace | Update++data UpsertMode = UseUpsert | NoUpsert++-- | Send or extend beamtime metadata+sendBeamtimeMetadata :: Producer -> Metadata -> MetadataIngestMode -> UpsertMode -> (RequestResponse -> IO ()) -> IO (Either Error Int)+sendBeamtimeMetadata (Producer producer) (Metadata metadata) ingestMode upsertMode callback = do+  requestCallback <- createRequestCallback (sendRequestCallback callback)+  (fromIntegral <$>)+    <$> withConstText metadata \metadataC ->+      checkError+        ( asapo_producer_send_beamtime_metadata+            producer+            metadataC+            ( case ingestMode of+                Insert -> kInsert+                Replace -> kReplace+                Update -> kUpdate+            )+            ( case upsertMode of+                UseUpsert -> 1+                _ -> 0+            )+            requestCallback+        )++-- | Send or extend stream metadata+sendStreamMetadata :: Producer -> Metadata -> MetadataIngestMode -> UpsertMode -> StreamName -> (RequestResponse -> IO ()) -> IO (Either Error Int)+sendStreamMetadata (Producer producer) (Metadata metadata) ingestMode upsertMode (StreamName stream) callback = do+  requestCallback <- createRequestCallback (sendRequestCallback callback)+  (fromIntegral <$>)+    <$> withConstText metadata \metadataC -> withConstText stream \streamC ->+      checkError+        ( asapo_producer_send_stream_metadata+            producer+            metadataC+            ( case ingestMode of+                Insert -> kInsert+                Replace -> kReplace+                Update -> kUpdate+            )+            ( case upsertMode of+                UseUpsert -> 1+                _ -> 0+            )+            streamC+            requestCallback+        )++data LogLevel+  = LogNone+  | LogError+  | LogInfo+  | LogDebug+  | LogWarning+  deriving (Eq)++convertLogLevel :: LogLevel -> AsapoLogLevel+convertLogLevel x | x == LogNone = asapoLogLevelNone+convertLogLevel x | x == LogError = asapoLogLevelError+convertLogLevel x | x == LogInfo = asapoLogLevelInfo+convertLogLevel x | x == LogDebug = asapoLogLevelDebug+convertLogLevel _ = asapoLogLevelWarning++-- | Set the log level+setLogLevel :: Producer -> LogLevel -> IO ()+setLogLevel (Producer producer) logLevel = asapo_producer_set_log_level producer (convertLogLevel logLevel)++-- | Enable/Disable logging to stdout+enableLocalLog :: Producer -> Bool -> IO ()+enableLocalLog (Producer producer) enable = asapo_producer_enable_local_log producer (if enable then 1 else 0)++-- | Enable/Disable logging to the central server+enableRemoteLog :: Producer -> Bool -> IO ()+enableRemoteLog (Producer producer) enable = asapo_producer_enable_remote_log producer (if enable then 1 else 0)++-- | Set a different set of credentials+setCredentials :: Producer -> SourceCredentials -> IO (Either Error Int)+setCredentials (Producer producer) credentials = withCredentials credentials \credentialsHandle ->+  (fromIntegral <$>) <$> checkError (asapo_producer_set_credentials producer credentialsHandle)++-- | Get current size of the requests queue (number of requests pending/being processed)+getRequestsQueueSize :: Producer -> IO Int+getRequestsQueueSize (Producer producer) = fromIntegral <$> asapo_producer_get_requests_queue_size producer++-- | Get current volume of the requests queue (total memory of occupied by pending/being processed requests)+getRequestsQueueVolumeMb :: Producer -> IO Int+getRequestsQueueVolumeMb (Producer producer) = fromIntegral <$> asapo_producer_get_requests_queue_volume_mb producer++-- | Set maximum size of the requests queue+setRequestsQueueLimits ::+  Producer ->+  -- | Size (0 for unlimited)+  Int ->+  -- | Volume (in MiB; 0 for unlimited)+  Int ->+  IO ()+setRequestsQueueLimits (Producer producer) size volume =+  asapo_producer_set_requests_queue_limits producer (fromIntegral size) (fromIntegral volume)++-- | Wait for all outstanding requests to finish+waitRequestsFinished :: Producer -> NominalDiffTime -> IO (Either Error Int)+waitRequestsFinished (Producer producer) timeout = do+  (fromIntegral <$>) <$> checkError (asapo_producer_wait_requests_finished producer (nominalDiffToMillis timeout))
+ lib/Asapo/Producer.hs view
@@ -0,0 +1,331 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS_GHC -Wno-missing-import-lists #-}++-- |+-- Description : High-level interface for all producer-related functions, using exceptions instead of @Either@+--+-- To implement an ASAP:O producer, you should only need this interface.+-- It exposes no memory-management functions (like free) or pointers, and+-- is thus safe to use.+--+-- = Simple Example+--+-- Here's some code for a simple producer that connects and sends a message with id "1".+--+-- >>> :seti -XOverloadedStrings+-- >>> :{+--  import Asapo.Producer+--  import Control.Applicative (Applicative ((<*>)))+--  import Control.Monad(void)+--  import Data.Either (Either (Left, Right))+--  import Data.Function (($))+--  import Data.Functor ((<$>))+--  import Data.Semigroup (Semigroup ((<>)))+--  import Data.Text (Text, pack)+--  import Data.Text.Encoding (encodeUtf8)+--  import qualified Data.Text.IO as TIO+--  import Data.Time.Clock (secondsToNominalDiffTime)+--  import Data.Word (Word64)+--  import System.IO (IO)+--  import Text.Show (Show (show))+--  import Prelude ()+--  main :: IO ()+--  main =+--    withProducer+--      (Endpoint "localhost:8400")+--      (ProcessingThreads 1)+--      TcpHandler+--      ( SourceCredentials+--          { sourceType = RawSource,+--            instanceId = InstanceId "test_instance",+--            pipelineStep = PipelineStep "pipeline_step_1",+--            beamtime = Beamtime "asapo_test",+--            beamline = Beamline "auto",+--            dataSource = DataSource "asapo_source",+--            token = Token "sometoken"+--          }+--      )+--      (secondsToNominalDiffTime 10) $ \producer -> do+--        TIO.putStrLn "connected, sending data"+--        let responseHandler :: RequestResponse -> IO ()+--            responseHandler requestResponse =+--              TIO.putStrLn $ "in response handler, payload "+--                <> responsePayload requestResponse+--                <> ", error "+--                <> pack (show (responseError requestResponse))+--        send+--          producer+--          (MessageId 1)+--          (FileName "raw/default/1.txt")+--          (Metadata "{\"test\": 3.0}")+--          (DatasetSubstream 0)+--          (DatasetSize 0)+--          NoAutoId+--          (encodeUtf8 "test")+--          DataAndMetadata+--          FilesystemAndDatabase+--          (StreamName "default")+--          responseHandler+--        void $ waitRequestsFinished producer (secondsToNominalDiffTime 10)+-- :}+module Asapo.Producer+  ( -- * Types+    ProducerException (..),+    Endpoint (..),+    ProcessingThreads (..),+    RequestHandlerType (..),+    Error (..),+    Metadata (..),+    SourceCredentials (..),+    MessageId (..),+    DeletionFlags (..),+    Producer,+    LogLevel (..),+    FileName (..),+    PipelineStep (..),+    Beamtime (Beamtime),+    DataSource (DataSource),+    Beamline (Beamline),+    StreamInfo (..),+    SourceType (..),+    StreamName (..),+    InstanceId (..),+    Token (..),+    DatasetSubstream (..),+    DatasetSize (..),+    VersionInfo (..),+    UpsertMode (..),+    MetadataIngestMode (..),+    AutoIdFlag (..),+    TransferFlag (..),+    StorageFlag (..),+    RequestResponse (..),+    Opcode (..),+    GenericRequestHeader (..),++    -- * Initialization+    withProducer,++    -- * Getters+    getVersionInfo,+    getStreamInfo,+    getLastStream,+    getBeamtimeMeta,+    getStreamMeta,+    getRequestsQueueSize,+    getRequestsQueueVolumeMb,++    -- * Modifiers+    send,+    sendFile,+    sendStreamFinishedFlag,+    sendBeamtimeMetadata,+    sendStreamMetadata,+    deleteStream,+    setLogLevel,+    enableLocalLog,+    enableRemoteLog,+    setCredentials,+    setRequestsQueueLimits,+    messageIdFromInt,+    waitRequestsFinished,+  )+where++import Asapo.Either.Common+  ( Beamline (Beamline),+    Beamtime (Beamtime),+    DataSource (DataSource),+    InstanceId (..),+    MessageId (..),+    PipelineStep (..),+    SourceCredentials (..),+    SourceType (..),+    StreamInfo (..),+    StreamName (..),+    Token (..),+    messageIdFromInt,+  )+import Asapo.Either.Producer+  ( AutoIdFlag,+    DatasetSize,+    DatasetSubstream,+    DeletionFlags,+    Endpoint,+    Error (Error),+    FileName,+    GenericRequestHeader (..),+    LogLevel (..),+    Metadata,+    MetadataIngestMode,+    Opcode (..),+    ProcessingThreads,+    Producer,+    RequestHandlerType,+    RequestResponse,+    StorageFlag,+    TransferFlag,+    UpsertMode,+    VersionInfo,+    enableLocalLog,+    enableRemoteLog,+    getRequestsQueueSize,+    getRequestsQueueVolumeMb,+    setLogLevel,+    setRequestsQueueLimits,+  )+import qualified Asapo.Either.Producer as PlainProducer+import Control.Applicative (pure)+import Control.Exception (Exception, throw)+import Control.Monad (Monad)+import qualified Data.ByteString as BS+import Data.Either (Either (Left, Right))+import Data.Int (Int)+import Data.Maybe (Maybe)+import Data.Text (Text)+import Data.Time (NominalDiffTime)+import System.IO (IO)+import Text.Show (Show)+import Prelude ()++newtype ProducerException = ProducerException Text deriving (Show)++instance Exception ProducerException++-- | Create a producer and do something with it. This is the main entrypoint into the producer.+withProducer ::+  forall a.+  Endpoint ->+  ProcessingThreads ->+  RequestHandlerType ->+  SourceCredentials ->+  -- | timeout+  NominalDiffTime ->+  (Producer -> IO a) ->+  IO a+withProducer endpoint processingThreads handlerType sourceCredentials timeout onSuccess =+  let onError (Error message) = throw (ProducerException message)+   in PlainProducer.withProducer endpoint processingThreads handlerType sourceCredentials timeout onError onSuccess++maybeThrow :: (Monad m) => m (Either Error b) -> m b+maybeThrow f = do+  result <- f+  case result of+    Left (Error message) -> throw (ProducerException message)+    Right v -> pure v++-- | Retrieve producer version info+getVersionInfo :: Producer -> IO VersionInfo+getVersionInfo producer = maybeThrow (PlainProducer.getVersionInfo producer)++-- | Retrieve info for a single stream+getStreamInfo ::+  Producer ->+  StreamName ->+  -- | Timeout+  NominalDiffTime ->+  IO StreamInfo+getStreamInfo producer stream timeout = maybeThrow (PlainProducer.getStreamInfo producer stream timeout)++-- | Retrieve info for the latest stream+getLastStream ::+  Producer ->+  -- | Timeout+  NominalDiffTime ->+  IO StreamInfo+getLastStream producer timeout = maybeThrow (PlainProducer.getLastStream producer timeout)++-- | Retrieve metadata for the given stream (which might be missing, in which case @Nothing@ is returned)+getStreamMeta ::+  Producer ->+  StreamName ->+  -- | timeout+  NominalDiffTime ->+  IO (Maybe Text)+getStreamMeta producer stream timeout = maybeThrow (PlainProducer.getStreamMeta producer stream timeout)++-- | Retrieve metadata for the given stream (which might be missing, in which case @Nothing@ is returned)+getBeamtimeMeta ::+  Producer ->+  -- | timeout+  NominalDiffTime ->+  IO (Maybe Text)+getBeamtimeMeta producer timeout = maybeThrow (PlainProducer.getBeamtimeMeta producer timeout)++deleteStream ::+  Producer ->+  StreamName ->+  -- | timeout+  NominalDiffTime ->+  [DeletionFlags] ->+  IO Int+deleteStream producer stream timeout deletionFlags = maybeThrow (PlainProducer.deleteStream producer stream timeout deletionFlags)++-- | Send a message containing raw data. Due to newtype and enum usage, all parameter should be self-explanatory+send ::+  Producer ->+  MessageId ->+  FileName ->+  Metadata ->+  DatasetSubstream ->+  DatasetSize ->+  AutoIdFlag ->+  -- | Actual data to send+  BS.ByteString ->+  TransferFlag ->+  StorageFlag ->+  StreamName ->+  (RequestResponse -> IO ()) ->+  IO Int+send producer messageId fileName metadata datasetSubstream datasetSize autoIdFlag data' transferFlag storageFlag stream callback =+  maybeThrow (PlainProducer.send producer messageId fileName metadata datasetSubstream datasetSize autoIdFlag data' transferFlag storageFlag stream callback)++-- | Send a message containing a file. Due to newtype and enum usage, all parameter should be self-explanatory+sendFile ::+  Producer ->+  MessageId ->+  -- | File name to put into the message header+  FileName ->+  Metadata ->+  DatasetSubstream ->+  DatasetSize ->+  AutoIdFlag ->+  -- | Size+  Int ->+  -- | File to actually send+  FileName ->+  TransferFlag ->+  StorageFlag ->+  StreamName ->+  (RequestResponse -> IO ()) ->+  IO Int+sendFile producer messageId fileName meta datasetSubstream datasetSize autoIdFlag size fileNameToSend transferFlag storageFlag stream callback =+  maybeThrow (PlainProducer.sendFile producer messageId fileName meta datasetSubstream datasetSize autoIdFlag size fileNameToSend transferFlag storageFlag stream callback)++-- | As the title says, send the "stream finished" flag+sendStreamFinishedFlag :: Producer -> StreamName -> MessageId -> StreamName -> (RequestResponse -> IO ()) -> IO Int+sendStreamFinishedFlag producer stream lastId nextStream callback =+  maybeThrow+    ( PlainProducer.sendStreamFinishedFlag+        producer+        stream+        lastId+        nextStream+        callback+    )++-- | Send or extend beamtime metadata+sendBeamtimeMetadata :: Producer -> Metadata -> MetadataIngestMode -> UpsertMode -> (RequestResponse -> IO ()) -> IO Int+sendBeamtimeMetadata producer metadata ingestMode upsertMode callback = maybeThrow (PlainProducer.sendBeamtimeMetadata producer metadata ingestMode upsertMode callback)++-- | Send or extend stream metadata+sendStreamMetadata :: Producer -> Metadata -> MetadataIngestMode -> UpsertMode -> StreamName -> (RequestResponse -> IO ()) -> IO Int+sendStreamMetadata producer metadata ingestMode upsertMode stream callback = maybeThrow (PlainProducer.sendStreamMetadata producer metadata ingestMode upsertMode stream callback)++-- | Set a different set of credentials+setCredentials :: Producer -> SourceCredentials -> IO Int+setCredentials producer creds = maybeThrow (PlainProducer.setCredentials producer creds)++-- | Wait for all outstanding requests to finish+waitRequestsFinished :: Producer -> NominalDiffTime -> IO Int+waitRequestsFinished producer timeout = maybeThrow (PlainProducer.waitRequestsFinished producer timeout)
+ lib/Asapo/Raw/Common.hs view
@@ -0,0 +1,169 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE CApiFFI #-}+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}++{-# HLINT ignore "Use camelCase" #-}++module Asapo.Raw.Common+  ( AsapoBool,+    AsapoSourceCredentialsHandle (AsapoSourceCredentialsHandle),+    AsapoMessageDataHandle (AsapoMessageDataHandle),+    asapo_is_error,+    AsapoSourceType,+    asapo_string_c_str,+    mkAsapoFreeWrapper,+    asapo_message_data_get_as_chars,+    asapo_free_stream_infos_handle,+    asapo_new_string_handle,+    asapo_free_string_handle,+    asapo_free_stream_info_handle,+    asapo_free_message_data_handle,+    asapo_error_explain,+    asapo_string_size,+    AsapoErrorHandle (AsapoErrorHandle),+    AsapoStreamInfosHandle (AsapoStreamInfosHandle),+    AsapoStringHandle (AsapoStringHandle),+    ConstCString,+    asapo_free_source_credentials,+    asapo_new_error_handle,+    asapo_free_error_handle,+    asapo_string_from_c_str,+    asapo_new_handle,+    asapo_free_handle,+    asapo_new_message_data_handle,+    asapo_create_source_credentials,+    AsapoStreamInfoHandle (AsapoStreamInfoHandle),+    asapo_stream_infos_get_item,+    asapo_stream_info_get_last_id,+    asapo_stream_info_get_name,+    asapo_stream_info_get_finished,+    asapo_stream_info_get_next_stream,+    asapo_stream_info_get_timestamp_created,+    asapo_stream_info_get_timestamp_last_entry,+    kProcessed,+    kRaw,+  )+where++import Data.Functor ((<$>))+import Foreign (FunPtr, with)+import Foreign.C.ConstPtr (ConstPtr (ConstPtr))+import Foreign.C.String (CString)+import Foreign.C.Types (CChar, CInt (CInt), CSize (CSize), CULong (CULong))+import Foreign.Ptr (Ptr)+import Foreign.Storable (Storable)+import System.Clock (TimeSpec)+import System.IO (IO)+import Prelude ()++-- common+type ConstCString = ConstPtr CChar++type AsapoBool = CInt++newtype {-# CTYPE "asapo/common/common_c.h" "AsapoSourceCredentialsHandle" #-} AsapoSourceCredentialsHandle = AsapoSourceCredentialsHandle (Ptr ()) deriving (Storable)++newtype {-# CTYPE "asapo/common/common_c.h" "AsapoErrorHandle" #-} AsapoErrorHandle = AsapoErrorHandle (Ptr ()) deriving (Storable)++newtype {-# CTYPE "asapo/common/common_c.h" "AsapoStringHandle" #-} AsapoStringHandle = AsapoStringHandle (Ptr ()) deriving (Storable)++asapo_new_string_handle :: IO AsapoStringHandle+asapo_new_string_handle = AsapoStringHandle <$> asapo_new_handle++asapo_free_string_handle :: AsapoStringHandle -> IO ()+asapo_free_string_handle (AsapoStringHandle ptr) = with ptr asapo_free_handle++-- -- FIXME: this is a custom function I added+-- -- foreign import capi "asapo/common/common_c.h &asapo_free_handle___" p_asapo_free_handle :: FunPtr (Ptr () -> IO ())++-- foreign import capi "asapo/common/common_c.h &hs_asapo_free_handle_with_ptr___" p_asapo_free_handle :: FunPtr (Ptr () -> IO ())++newtype {-# CTYPE "asapo/common/common_c.h" "AsapoStreamInfoHandle" #-} AsapoStreamInfoHandle = AsapoStreamInfoHandle (Ptr ()) deriving (Storable)++asapo_free_stream_info_handle :: AsapoStreamInfoHandle -> IO ()+asapo_free_stream_info_handle (AsapoStreamInfoHandle ptr) = with ptr asapo_free_handle++asapo_free_stream_infos_handle :: AsapoStreamInfosHandle -> IO ()+asapo_free_stream_infos_handle (AsapoStreamInfosHandle ptr) = with ptr asapo_free_handle++newtype {-# CTYPE "asapo/common/common_c.h" "AsapoStreamInfosHandle" #-} AsapoStreamInfosHandle = AsapoStreamInfosHandle (Ptr ()) deriving (Storable)++newtype {-# CTYPE "asapo/consumer_c.h" "AsapoMessageDataHandle" #-} AsapoMessageDataHandle = AsapoMessageDataHandle (Ptr ()) deriving (Storable)++asapo_new_message_data_handle :: IO AsapoMessageDataHandle+asapo_new_message_data_handle = AsapoMessageDataHandle <$> asapo_new_handle++asapo_free_message_data_handle :: AsapoMessageDataHandle -> IO ()+asapo_free_message_data_handle (AsapoMessageDataHandle ptr) = with ptr asapo_free_handle++type AsapoSourceType = CInt++foreign import capi "asapo/common/common_c.h value kProcessed" kProcessed :: AsapoSourceType++foreign import capi "asapo/common/common_c.h value kRaw" kRaw :: AsapoSourceType++foreign import capi "asapo/common/common_c.h asapo_free_handle__" asapo_free_handle :: Ptr (Ptr ()) -> IO ()++foreign import ccall "wrapper" mkAsapoFreeWrapper :: (Ptr () -> IO ()) -> IO (FunPtr (Ptr () -> IO ()))++foreign import capi "asapo/common/common_c.h asapo_new_handle" asapo_new_handle :: IO (Ptr ())++asapo_new_error_handle :: IO AsapoErrorHandle+asapo_new_error_handle = AsapoErrorHandle <$> asapo_new_handle++asapo_free_error_handle :: AsapoErrorHandle -> IO ()+asapo_free_error_handle (AsapoErrorHandle ptr) = with ptr asapo_free_handle++foreign import capi "asapo/common/common_c.h asapo_error_explain" asapo_error_explain :: AsapoErrorHandle -> CString -> CSize -> IO ()++foreign import capi "asapo/common/common_c.h asapo_is_error" asapo_is_error :: AsapoErrorHandle -> IO AsapoBool++foreign import capi "asapo/common/common_c.h asapo_string_from_c_str" asapo_string_from_c_str :: ConstCString -> IO AsapoStringHandle++foreign import capi "asapo/common/common_c.h asapo_string_c_str" asapo_string_c_str :: AsapoStringHandle -> IO ConstCString++foreign import capi "asapo/common/common_c.h asapo_string_size" asapo_string_size :: AsapoStringHandle -> IO CSize++foreign import capi "asapo/common/common_c.h asapo_stream_infos_get_item"+  asapo_stream_infos_get_item ::+    AsapoStreamInfosHandle ->+    -- index+    CSize ->+    IO AsapoStreamInfoHandle++foreign import capi "asapo/common/common_c.h asapo_stream_info_get_last_id" asapo_stream_info_get_last_id :: AsapoStreamInfoHandle -> IO CULong++foreign import capi "asapo/common/common_c.h asapo_stream_info_get_name" asapo_stream_info_get_name :: AsapoStreamInfoHandle -> IO ConstCString++-- Yes, this has a typo. Corrected in the Haskell function name+foreign import capi "asapo/common/common_c.h asapo_stream_info_get_ffinished" asapo_stream_info_get_finished :: AsapoStreamInfoHandle -> IO AsapoBool++foreign import capi "asapo/common/common_c.h asapo_stream_info_get_next_stream" asapo_stream_info_get_next_stream :: AsapoStreamInfoHandle -> IO ConstCString++foreign import capi "asapo/common/common_c.h asapo_stream_info_get_timestamp_created" asapo_stream_info_get_timestamp_created :: AsapoStreamInfoHandle -> Ptr TimeSpec -> IO ()++foreign import capi "asapo/common/common_c.h asapo_stream_info_get_timestamp_last_entry" asapo_stream_info_get_timestamp_last_entry :: AsapoStreamInfoHandle -> Ptr TimeSpec -> IO ()++foreign import capi "asapo/common/common_c.h asapo_create_source_credentials"+  asapo_create_source_credentials ::+    AsapoSourceType ->+    -- instance ID+    CString ->+    -- pipeline step+    CString ->+    -- beamtime+    CString ->+    -- beamline+    CString ->+    -- data source+    CString ->+    -- token+    CString ->+    IO AsapoSourceCredentialsHandle++asapo_free_source_credentials :: AsapoSourceCredentialsHandle -> IO ()+asapo_free_source_credentials (AsapoSourceCredentialsHandle ptr) = with ptr asapo_free_handle++foreign import capi "asapo/common/common_c.h asapo_message_data_get_as_chars" asapo_message_data_get_as_chars :: AsapoMessageDataHandle -> IO ConstCString
+ lib/Asapo/Raw/Consumer.hs view
@@ -0,0 +1,485 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE CApiFFI #-}+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}++{-# HLINT ignore "Use camelCase" #-}++module Asapo.Raw.Consumer+  ( AsapoConsumerHandle (AsapoConsumerHandle),+    AsapoMessageMetaHandle (AsapoMessageMetaHandle),+    AsapoMessageMetasHandle (AsapoMessageMetasHandle),+    AsapoIdListHandle (AsapoIdListHandle),+    AsapoDataSetHandle (AsapoDataSetHandle),+    AsapoPartialErrorDataHandle (AsapoPartialErrorDataHandle),+    AsapoConsumerErrorDataHandle (AsapoConsumerErrorDataHandle),+    AsapoConsumerErrorType,+    asapo_stream_infos_get_size,+    asapo_new_message_meta_handle,+    asapo_free_consumer_handle,+    asapo_free_message_metas_handle,+    asapo_free_id_list_handle,+    kNoData,+    kEndOfStream,+    kStreamFinished,+    kUnavailableService,+    kInterruptedTransaction,+    kLocalIOError,+    kWrongInput,+    kPartialData,+    kUnsupportedClient,+    kDataNotInCache,+    kUnknownError,+    AsapoStreamFilter,+    kAllStreams,+    kFinishedStreams,+    kUnfinishedStreams,+    AsapoNetworkConnectionType,+    kUndefined,+    kAsapoTcp,+    kFabric,+    asapo_error_get_type,+    asapo_create_consumer,+    asapo_consumer_generate_new_group_id,+    asapo_consumer_set_timeout,+    asapo_consumer_reset_last_read_marker,+    asapo_consumer_set_last_read_marker,+    asapo_consumer_acknowledge,+    asapo_consumer_negative_acknowledge,+    asapo_consumer_get_unacknowledged_messages,+    asapo_id_list_get_size,+    asapo_id_list_get_item,+    asapo_consumer_get_last_acknowledged_message,+    asapo_consumer_current_connection_type,+    asapo_consumer_get_stream_list,+    asapo_consumer_delete_stream,+    asapo_consumer_set_stream_persistent,+    asapo_consumer_get_current_size,+    asapo_consumer_get_current_dataset_count,+    asapo_consumer_get_beamtime_meta,+    asapo_consumer_retrieve_data,+    asapo_consumer_get_next_dataset,+    asapo_consumer_get_last_dataset,+    asapo_consumer_get_last_dataset_ingroup,+    asapo_consumer_get_by_id,+    asapo_consumer_get_last,+    asapo_consumer_get_last_ingroup,+    asapo_consumer_get_next,+    asapo_consumer_query_messages,+    asapo_consumer_set_resend_nacs,+    asapo_message_data_get_as_chars,+    asapo_message_meta_get_name,+    asapo_message_meta_get_timestamp,+    asapo_message_meta_get_size,+    asapo_message_meta_get_id,+    asapo_message_meta_get_source,+    asapo_message_meta_get_metadata,+    asapo_message_meta_get_buf_id,+    asapo_message_meta_get_dataset_substream,+    asapo_dataset_get_id,+    asapo_dataset_get_expected_size,+    asapo_dataset_get_size,+    asapo_dataset_get_item,+    asapo_message_metas_get_size,+    asapo_message_metas_get_item,+    asapo_error_get_payload_from_partial_error,+    asapo_partial_error_get_id,+    asapo_partial_error_get_expected_size,+    asapo_error_get_payload_from_consumer_error,+    asapo_consumer_error_get_id,+    asapo_consumer_error_get_next_stream,+  )+where++import Asapo.Raw.Common+  ( AsapoBool,+    AsapoErrorHandle (AsapoErrorHandle),+    AsapoMessageDataHandle (AsapoMessageDataHandle),+    AsapoSourceCredentialsHandle (AsapoSourceCredentialsHandle),+    AsapoStreamInfosHandle (AsapoStreamInfosHandle),+    AsapoStringHandle (AsapoStringHandle),+    ConstCString,+    asapo_free_handle,+    asapo_new_handle,+  )+import Data.Functor ((<$>))+import Data.Int (Int64)+import Data.Word (Word64)+import Foreign.C.ConstPtr (ConstPtr (ConstPtr))+import Foreign.C.Types (CInt (CInt), CLong (CLong), CSize (CSize))+import Foreign.Marshal (with)+import Foreign.Ptr (Ptr)+import Foreign.Storable (Storable)+import System.Clock (TimeSpec)+import System.IO (IO)+import Prelude ()++foreign import capi "asapo/consumer_c.h asapo_stream_infos_get_size" asapo_stream_infos_get_size :: AsapoStreamInfosHandle -> IO CSize++newtype {-# CTYPE "asapo/consumer_c.h" "AsapoConsumerHandle" #-} AsapoConsumerHandle = AsapoConsumerHandle (Ptr ()) deriving (Storable)++newtype {-# CTYPE "asapo/consumer_c.h" "AsapoMessageMetaHandle" #-} AsapoMessageMetaHandle = AsapoMessageMetaHandle (Ptr ()) deriving (Storable)++newtype {-# CTYPE "asapo/consumer_c.h" "AsapoMessageMetasHandle" #-} AsapoMessageMetasHandle = AsapoMessageMetasHandle (Ptr ()) deriving (Storable)++asapo_free_consumer_handle :: AsapoConsumerHandle -> IO ()+asapo_free_consumer_handle (AsapoConsumerHandle ptr) = with ptr asapo_free_handle++asapo_free_message_metas_handle :: AsapoMessageMetasHandle -> IO ()+asapo_free_message_metas_handle (AsapoMessageMetasHandle ptr) = with ptr asapo_free_handle++asapo_new_message_meta_handle :: IO AsapoMessageMetaHandle+asapo_new_message_meta_handle = AsapoMessageMetaHandle <$> asapo_new_handle++newtype {-# CTYPE "asapo/consumer_c.h" "AsapoIdListHandle" #-} AsapoIdListHandle = AsapoIdListHandle (Ptr ()) deriving (Storable)++asapo_free_id_list_handle :: AsapoIdListHandle -> IO ()+asapo_free_id_list_handle (AsapoIdListHandle ptr) = with ptr asapo_free_handle++newtype {-# CTYPE "asapo/consumer_c.h" "AsapoDataSetHandle" #-} AsapoDataSetHandle = AsapoDataSetHandle (Ptr ()) deriving (Storable)++newtype {-# CTYPE "asapo/consumer_c.h" "AsapoPartialErrorDataHandle" #-} AsapoPartialErrorDataHandle = AsapoPartialErrorDataHandle (Ptr ()) deriving (Storable)++newtype {-# CTYPE "asapo/consumer_c.h" "AsapoConsumerErrorDataHandle" #-} AsapoConsumerErrorDataHandle = AsapoConsumerErrorDataHandle (Ptr ()) deriving (Storable)++type AsapoConsumerErrorType = CInt++foreign import capi "asapo/consumer_c.h value kNoData" kNoData :: AsapoConsumerErrorType++foreign import capi "asapo/consumer_c.h value kEndOfStream" kEndOfStream :: AsapoConsumerErrorType++foreign import capi "asapo/consumer_c.h value kStreamFinished" kStreamFinished :: AsapoConsumerErrorType++foreign import capi "asapo/consumer_c.h value kUnavailableService" kUnavailableService :: AsapoConsumerErrorType++foreign import capi "asapo/consumer_c.h value kInterruptedTransaction" kInterruptedTransaction :: AsapoConsumerErrorType++foreign import capi "asapo/consumer_c.h value kLocalIOError" kLocalIOError :: AsapoConsumerErrorType++foreign import capi "asapo/consumer_c.h value kWrongInput" kWrongInput :: AsapoConsumerErrorType++foreign import capi "asapo/consumer_c.h value kPartialData" kPartialData :: AsapoConsumerErrorType++foreign import capi "asapo/consumer_c.h value kUnsupportedClient" kUnsupportedClient :: AsapoConsumerErrorType++foreign import capi "asapo/consumer_c.h value kDataNotInCache" kDataNotInCache :: AsapoConsumerErrorType++foreign import capi "asapo/consumer_c.h value kUnknownError" kUnknownError :: AsapoConsumerErrorType++type AsapoStreamFilter = CInt++foreign import capi "asapo/consumer_c.h value kAllStreams" kAllStreams :: AsapoStreamFilter++foreign import capi "asapo/consumer_c.h value kFinishedStreams" kFinishedStreams :: AsapoStreamFilter++foreign import capi "asapo/consumer_c.h value kUnfinishedStreams" kUnfinishedStreams :: AsapoStreamFilter++type AsapoNetworkConnectionType = CInt++foreign import capi "asapo/consumer_c.h value kUndefined" kUndefined :: AsapoNetworkConnectionType++foreign import capi "asapo/consumer_c.h value kAsapoTcp" kAsapoTcp :: AsapoNetworkConnectionType++foreign import capi "asapo/consumer_c.h value kFabric" kFabric :: AsapoNetworkConnectionType++foreign import capi "asapo/consumer_c.h asapo_error_get_type" asapo_error_get_type :: AsapoErrorHandle -> IO AsapoConsumerErrorType++foreign import capi "asapo/consumer_c.h asapo_create_consumer"+  asapo_create_consumer ::+    -- server_name+    ConstCString ->+    -- source_path+    ConstCString ->+    -- has filesystem+    AsapoBool ->+    AsapoSourceCredentialsHandle ->+    Ptr AsapoErrorHandle ->+    IO AsapoConsumerHandle++foreign import capi "asapo/consumer_c.h asapo_consumer_generate_new_group_id" asapo_consumer_generate_new_group_id :: AsapoConsumerHandle -> Ptr AsapoErrorHandle -> IO AsapoStringHandle++foreign import capi "asapo/consumer_c.h asapo_consumer_set_timeout" asapo_consumer_set_timeout :: AsapoConsumerHandle -> Word64 -> IO ()++foreign import capi "asapo/consumer_c.h asapo_consumer_reset_last_read_marker"+  asapo_consumer_reset_last_read_marker ::+    AsapoConsumerHandle ->+    -- group_id+    AsapoStringHandle ->+    -- stream+    ConstCString ->+    Ptr AsapoErrorHandle ->+    IO CInt++foreign import capi "asapo/consumer_c.h asapo_consumer_set_last_read_marker"+  asapo_consumer_set_last_read_marker ::+    AsapoConsumerHandle ->+    -- group_id+    AsapoStringHandle ->+    -- value+    Word64 ->+    -- stream+    ConstCString ->+    Ptr AsapoErrorHandle ->+    IO CInt++foreign import capi "asapo/consumer_c.h asapo_consumer_acknowledge"+  asapo_consumer_acknowledge ::+    AsapoConsumerHandle ->+    -- group_id+    AsapoStringHandle ->+    -- id+    Word64 ->+    -- stream+    ConstCString ->+    Ptr AsapoErrorHandle ->+    IO CInt++foreign import capi "asapo/consumer_c.h asapo_consumer_negative_acknowledge"+  asapo_consumer_negative_acknowledge ::+    AsapoConsumerHandle ->+    -- group_id+    AsapoStringHandle ->+    -- id+    Word64 ->+    -- delay_ms+    Word64 ->+    -- stream+    ConstCString ->+    Ptr AsapoErrorHandle ->+    IO CInt++foreign import capi "asapo/consumer_c.h asapo_consumer_get_unacknowledged_messages"+  asapo_consumer_get_unacknowledged_messages ::+    AsapoConsumerHandle ->+    -- group_id+    AsapoStringHandle ->+    -- from id+    Word64 ->+    -- to id+    Word64 ->+    -- stream+    ConstCString ->+    Ptr AsapoErrorHandle ->+    IO AsapoIdListHandle++foreign import capi "asapo/consumer_c.h asapo_id_list_get_size" asapo_id_list_get_size :: AsapoIdListHandle -> IO CSize++foreign import capi "asapo/consumer_c.h asapo_id_list_get_item" asapo_id_list_get_item :: AsapoIdListHandle -> CSize -> IO Word64++foreign import capi "asapo/consumer_c.h asapo_consumer_get_last_acknowledged_message"+  asapo_consumer_get_last_acknowledged_message ::+    AsapoConsumerHandle ->+    -- group_id+    AsapoStringHandle ->+    -- stream+    ConstCString ->+    Ptr AsapoErrorHandle ->+    IO CLong++-- foreign import capi "asapo/consumer_c.h asapo_consumer_force_no_rdma" asapo_consumer_force_no_rdma :: AsapoConsumerHandle -> IO ()++foreign import capi "asapo/consumer_c.h asapo_consumer_current_connection_type" asapo_consumer_current_connection_type :: AsapoConsumerHandle -> IO AsapoNetworkConnectionType++-- Again, a typo in the original (no "asapo_" prefix, but corrected in the Haskell code)+-- foreign import capi "asapo/consumer_c.h enable_new_monitoring_api_format" asapo_enable_new_monitoring_api_format :: AsapoConsumerHandle -> AsapoBool -> Ptr AsapoErrorHandle -> IO CInt++foreign import capi "asapo/consumer_c.h asapo_consumer_get_stream_list"+  asapo_consumer_get_stream_list ::+    AsapoConsumerHandle ->+    -- stream+    ConstCString ->+    AsapoStreamFilter ->+    Ptr AsapoErrorHandle ->+    IO AsapoStreamInfosHandle++foreign import capi "asapo/consumer_c.h asapo_consumer_delete_stream"+  asapo_consumer_delete_stream ::+    AsapoConsumerHandle ->+    -- stream+    ConstCString ->+    -- delete_meta+    AsapoBool ->+    -- error_on_not_exist+    AsapoBool ->+    Ptr AsapoErrorHandle ->+    IO CInt++foreign import capi "asapo/consumer_c.h asapo_consumer_set_stream_persistent"+  asapo_consumer_set_stream_persistent ::+    AsapoConsumerHandle ->+    -- stream+    ConstCString ->+    Ptr AsapoErrorHandle ->+    IO CInt++foreign import capi "asapo/consumer_c.h asapo_consumer_get_current_size"+  asapo_consumer_get_current_size ::+    AsapoConsumerHandle ->+    -- stream+    ConstCString ->+    Ptr AsapoErrorHandle ->+    IO Int64++foreign import capi "asapo/consumer_c.h asapo_consumer_get_current_dataset_count"+  asapo_consumer_get_current_dataset_count ::+    AsapoConsumerHandle ->+    -- stream+    ConstCString ->+    -- include_incomplete+    AsapoBool ->+    Ptr AsapoErrorHandle ->+    IO Int64++foreign import capi "asapo/consumer_c.h asapo_consumer_get_beamtime_meta" asapo_consumer_get_beamtime_meta :: AsapoConsumerHandle -> Ptr AsapoErrorHandle -> IO AsapoStringHandle++foreign import capi "asapo/consumer_c.h asapo_consumer_retrieve_data"+  asapo_consumer_retrieve_data ::+    AsapoConsumerHandle ->+    AsapoMessageMetaHandle ->+    Ptr AsapoMessageDataHandle ->+    Ptr AsapoErrorHandle ->+    IO CInt++foreign import capi "asapo/consumer_c.h asapo_consumer_get_next_dataset"+  asapo_consumer_get_next_dataset ::+    AsapoConsumerHandle ->+    -- group_id+    AsapoStringHandle ->+    -- min_size+    Word64 ->+    -- stream+    ConstCString ->+    Ptr AsapoErrorHandle ->+    IO AsapoDataSetHandle++foreign import capi "asapo/consumer_c.h asapo_consumer_get_last_dataset"+  asapo_consumer_get_last_dataset ::+    AsapoConsumerHandle ->+    -- min_size+    Word64 ->+    -- stream+    ConstCString ->+    Ptr AsapoErrorHandle ->+    IO AsapoDataSetHandle++foreign import capi "asapo/consumer_c.h asapo_consumer_get_last_dataset_ingroup"+  asapo_consumer_get_last_dataset_ingroup ::+    AsapoConsumerHandle ->+    -- group_id+    AsapoStringHandle ->+    -- min_size+    Word64 ->+    -- stream+    ConstCString ->+    Ptr AsapoErrorHandle ->+    IO AsapoDataSetHandle++foreign import capi "asapo/consumer_c.h asapo_consumer_get_by_id"+  asapo_consumer_get_by_id ::+    AsapoConsumerHandle ->+    -- id+    Word64 ->+    Ptr AsapoMessageMetaHandle ->+    Ptr AsapoMessageDataHandle ->+    -- stream+    ConstCString ->+    Ptr AsapoErrorHandle ->+    IO CInt++foreign import capi "asapo/consumer_c.h asapo_consumer_get_last"+  asapo_consumer_get_last ::+    AsapoConsumerHandle ->+    Ptr AsapoMessageMetaHandle ->+    Ptr AsapoMessageDataHandle ->+    -- stream+    ConstCString ->+    Ptr AsapoErrorHandle ->+    IO CInt++foreign import capi "asapo/consumer_c.h asapo_consumer_get_last_ingroup"+  asapo_consumer_get_last_ingroup ::+    AsapoConsumerHandle ->+    -- group_id+    AsapoStringHandle ->+    Ptr AsapoMessageMetaHandle ->+    Ptr AsapoMessageDataHandle ->+    -- stream+    ConstCString ->+    Ptr AsapoErrorHandle ->+    IO CInt++foreign import capi "asapo/consumer_c.h asapo_consumer_get_next"+  asapo_consumer_get_next ::+    AsapoConsumerHandle ->+    -- group id+    AsapoStringHandle ->+    Ptr AsapoMessageMetaHandle ->+    Ptr AsapoMessageDataHandle ->+    -- stream+    ConstCString ->+    Ptr AsapoErrorHandle ->+    IO CInt++foreign import capi "asapo/consumer_c.h asapo_consumer_query_messages"+  asapo_consumer_query_messages ::+    AsapoConsumerHandle ->+    -- query+    ConstCString ->+    -- stream+    ConstCString ->+    Ptr AsapoErrorHandle ->+    IO AsapoMessageMetasHandle++foreign import capi "asapo/consumer_c.h asapo_consumer_set_resend_nacs"+  asapo_consumer_set_resend_nacs ::+    AsapoConsumerHandle ->+    -- resend+    AsapoBool ->+    -- delay_ms+    Word64 ->+    -- resend_attempts+    Word64 ->+    IO ()++foreign import capi "asapo/consumer_c.h asapo_message_data_get_as_chars" asapo_message_data_get_as_chars :: AsapoMessageDataHandle -> IO ConstCString++foreign import capi "asapo/consumer_c.h asapo_message_meta_get_name" asapo_message_meta_get_name :: AsapoMessageMetaHandle -> IO ConstCString++foreign import capi "asapo/consumer_c.h asapo_message_meta_get_timestamp" asapo_message_meta_get_timestamp :: AsapoMessageMetaHandle -> Ptr TimeSpec -> IO ()++foreign import capi "asapo/consumer_c.h asapo_message_meta_get_size" asapo_message_meta_get_size :: AsapoMessageMetaHandle -> IO Word64++foreign import capi "asapo/consumer_c.h asapo_message_meta_get_id" asapo_message_meta_get_id :: AsapoMessageMetaHandle -> IO Word64++foreign import capi "asapo/consumer_c.h asapo_message_meta_get_source" asapo_message_meta_get_source :: AsapoMessageMetaHandle -> IO ConstCString++foreign import capi "asapo/consumer_c.h asapo_message_meta_get_metadata" asapo_message_meta_get_metadata :: AsapoMessageMetaHandle -> IO ConstCString++foreign import capi "asapo/consumer_c.h asapo_message_meta_get_buf_id" asapo_message_meta_get_buf_id :: AsapoMessageMetaHandle -> IO Word64++foreign import capi "asapo/consumer_c.h asapo_message_meta_get_dataset_substream" asapo_message_meta_get_dataset_substream :: AsapoMessageMetaHandle -> IO Word64++foreign import capi "asapo/consumer_c.h asapo_dataset_get_id" asapo_dataset_get_id :: AsapoDataSetHandle -> IO Word64++foreign import capi "asapo/consumer_c.h asapo_dataset_get_expected_size" asapo_dataset_get_expected_size :: AsapoDataSetHandle -> IO Word64++foreign import capi "asapo/consumer_c.h asapo_dataset_get_size" asapo_dataset_get_size :: AsapoDataSetHandle -> IO CSize++foreign import capi "asapo/consumer_c.h asapo_dataset_get_item" asapo_dataset_get_item :: AsapoDataSetHandle -> CSize -> IO AsapoMessageMetaHandle++foreign import capi "asapo/consumer_c.h asapo_message_metas_get_size" asapo_message_metas_get_size :: AsapoMessageMetasHandle -> IO CSize++foreign import capi "asapo/consumer_c.h asapo_message_metas_get_item" asapo_message_metas_get_item :: AsapoMessageMetasHandle -> CSize -> IO AsapoMessageMetaHandle++foreign import capi "asapo/consumer_c.h asapo_error_get_payload_from_partial_error" asapo_error_get_payload_from_partial_error :: AsapoErrorHandle -> IO AsapoPartialErrorDataHandle++foreign import capi "asapo/consumer_c.h asapo_partial_error_get_id" asapo_partial_error_get_id :: AsapoPartialErrorDataHandle -> IO Word64++foreign import capi "asapo/consumer_c.h asapo_partial_error_get_expected_size" asapo_partial_error_get_expected_size :: AsapoPartialErrorDataHandle -> IO Word64++foreign import capi "asapo/consumer_c.h asapo_error_get_payload_from_consumer_error" asapo_error_get_payload_from_consumer_error :: AsapoErrorHandle -> IO AsapoConsumerErrorDataHandle++foreign import capi "asapo/consumer_c.h asapo_consumer_error_get_id" asapo_consumer_error_get_id :: AsapoConsumerErrorDataHandle -> IO Word64++foreign import capi "asapo/consumer_c.h asapo_consumer_error_get_next_stream" asapo_consumer_error_get_next_stream :: AsapoConsumerErrorDataHandle -> IO ConstCString
+ lib/Asapo/Raw/FreeHandleHack.hsc view
@@ -0,0 +1,19 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE CApiFFI #-}+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}++#include <asapo/common/common_c.h>++-- |+-- Description : Here be dragons+--+-- Actually, not really dragons, just a small hack in order to free up memory without patching asapo or writing a whole C module+module Asapo.Raw.FreeHandleHack(p_asapo_free_handle) where+import Foreign(Ptr, FunPtr)+import Prelude()+import System.IO(IO)++#def void hs_asapo_free_handle_with_ptr(void *handle) { asapo_free_handle__(&handle); }++foreign import capi "asapo/common/common_c.h &hs_asapo_free_handle_with_ptr" p_asapo_free_handle :: FunPtr (Ptr () -> IO ())
+ lib/Asapo/Raw/Producer.hsc view
@@ -0,0 +1,425 @@+{-# LANGUAGE CApiFFI #-}+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}++-- hsc2hs needs the include to find the C structure+#include <asapo/producer_c.h>++module Asapo.Raw.Producer+  ( AsapoProducerHandle (AsapoProducerHandle),+    AsapoRequestCallbackPayloadHandle (AsapoRequestCallbackPayloadHandle),+    AsapoMessageHeaderHandle (AsapoMessageHeaderHandle),+    AsapoRequestCallback,+    createRequestCallback,+    kMaxMessageSize,+    asapo_free_producer_handle,+    kNCustomParams,+    kDefaultIngestMode,+    kMaxVersionSize,+    asapo_free_message_header_handle,+    AsapoRequestHandlerType,+    kTcp,+    kFilesystem,+    AsapoIngestModeFlags,+    kTransferData,+    AsapoOpcode,+    kOpcodeUnknownOp,+    kOpcodeTransferData,+    kOpcodeTransferDatasetData,+    kOpcodeStreamInfo,+    kOpcodeLastStream,+    kOpcodeGetBufferData,+    kOpcodeAuthorize,+    kOpcodeTransferMetaData,+    kOpcodeDeleteStream,+    kOpcodeGetMeta,+    kOpcodeCount,+    kOpcodePersistStream,+    kTransferMetaDataOnly,+    kStoreInFilesystem,+    kStoreInDatabase,+    AsapoMetaIngestOp,+    kInsert,+    kReplace,+    kUpdate,+    AsapoLogLevel,+    asapoLogLevelNone,+    asapoLogLevelError,+    asapoLogLevelInfo,+    asapoLogLevelDebug,+    asapoLogLevelWarning,+    asapo_create_producer,+    asapo_producer_get_version_info,+    asapo_producer_get_stream_info,+    asapo_producer_get_stream_meta,+    asapo_producer_get_beamtime_meta,+    asapo_producer_delete_stream,+    asapo_producer_get_last_stream,+    asapo_create_message_header,+    asapo_producer_send,+    asapo_producer_send_file,+    asapo_producer_send_stream_finished_flag,+    asapo_producer_send_beamtime_metadata,+    asapo_producer_send_stream_metadata,+    asapo_request_callback_payload_get_response,+    asapo_request_callback_payload_get_original_header,+    asapo_producer_set_log_level,+    asapo_producer_enable_local_log,+    asapo_producer_enable_remote_log,+    asapo_producer_set_credentials,+    asapo_producer_get_requests_queue_size,+    asapo_producer_get_requests_queue_volume_mb,+    asapo_producer_set_requests_queue_limits,+    asapo_producer_wait_requests_finished,+    AsapoGenericRequestHeader(..)+  )+where++import Data.Functor((<$>))+import Data.Word(Word64)+import Asapo.Raw.Common+  ( AsapoBool,+    AsapoErrorHandle (AsapoErrorHandle),+    AsapoSourceCredentialsHandle (AsapoSourceCredentialsHandle),+    AsapoStreamInfoHandle (AsapoStreamInfoHandle),+    AsapoStringHandle (AsapoStringHandle),+    ConstCString,+    asapo_free_handle+  )+import Foreign.C.ConstPtr (ConstPtr (ConstPtr))+import Foreign.C.String (CString)+import Foreign.C.Types (CInt (CInt), CSize (CSize), CUChar (CUChar))+import Foreign.Ptr (FunPtr, Ptr, plusPtr)+import Foreign (with, peekArray)+import Foreign.Storable (Storable(alignment, peek, peekByteOff, poke, sizeOf))+import System.IO (IO)+import Prelude (error, fromIntegral)+import Control.Applicative((<*>), pure)++newtype {-# CTYPE "asapo/producer_c.h" "AsapoProducerHandle" #-} AsapoProducerHandle = AsapoProducerHandle (Ptr ()) deriving (Storable)++asapo_free_producer_handle :: AsapoProducerHandle -> IO ()+asapo_free_producer_handle (AsapoProducerHandle ptr) = with ptr \ptr' -> asapo_free_handle ptr'++newtype {-# CTYPE "asapo/producer_c.h" "AsapoRequestCallbackPayloadHandle" #-} AsapoRequestCallbackPayloadHandle = AsapoRequestCallbackPayloadHandle (Ptr ()) deriving (Storable)++newtype {-# CTYPE "asapo/producer_c.h" "AsapoMessageHeaderHandle" #-} AsapoMessageHeaderHandle = AsapoMessageHeaderHandle (Ptr ()) deriving (Storable)++asapo_free_message_header_handle :: AsapoMessageHeaderHandle -> IO ()+asapo_free_message_header_handle (AsapoMessageHeaderHandle ptr) = with ptr \ptr' -> asapo_free_handle ptr'++type AsapoRequestCallback = Ptr () -> AsapoRequestCallbackPayloadHandle -> AsapoErrorHandle -> IO ()++foreign import ccall "wrapper" createRequestCallback :: AsapoRequestCallback -> IO (FunPtr AsapoRequestCallback)++foreign import capi "asapo/producer_c.h value kMaxMessageSize" kMaxMessageSize :: CSize++foreign import capi "asapo/producer_c.h value kMaxVersionSize" kMaxVersionSize :: CSize++foreign import capi "asapo/producer_c.h value kNCustomParams" kNCustomParams :: CSize++type AsapoOpcode = CInt++foreign import capi "asapo/producer_c.h value kOpcodeUnknownOp" kOpcodeUnknownOp :: AsapoOpcode++foreign import capi "asapo/producer_c.h value kOpcodeTransferData" kOpcodeTransferData :: AsapoOpcode++foreign import capi "asapo/producer_c.h value kOpcodeTransferDatasetData" kOpcodeTransferDatasetData :: AsapoOpcode++foreign import capi "asapo/producer_c.h value kOpcodeStreamInfo" kOpcodeStreamInfo :: AsapoOpcode++foreign import capi "asapo/producer_c.h value kOpcodeLastStream" kOpcodeLastStream :: AsapoOpcode++foreign import capi "asapo/producer_c.h value kOpcodeGetBufferData" kOpcodeGetBufferData :: AsapoOpcode++foreign import capi "asapo/producer_c.h value kOpcodeAuthorize" kOpcodeAuthorize :: AsapoOpcode++foreign import capi "asapo/producer_c.h value kOpcodeTransferMetaData" kOpcodeTransferMetaData :: AsapoOpcode++foreign import capi "asapo/producer_c.h value kOpcodeDeleteStream" kOpcodeDeleteStream :: AsapoOpcode++foreign import capi "asapo/producer_c.h value kOpcodeGetMeta" kOpcodeGetMeta :: AsapoOpcode++foreign import capi "asapo/producer_c.h value kOpcodeCount" kOpcodeCount :: AsapoOpcode++foreign import capi "asapo/producer_c.h value kOpcodePersistStream" kOpcodePersistStream :: AsapoOpcode++data AsapoGenericRequestHeader = AsapoGenericRequestHeader+  { asapoGenericRequestHeaderOpCode :: !AsapoOpcode,+    asapoGenericRequestHeaderDataId :: !Word64,+    asapoGenericRequestHeaderDataSize :: !Word64,+    asapoGenericRequestHeaderMetaSize :: !Word64,+    asapoGenericRequestHeaderCustomData :: ![Word64],+    asapoGenericRequestHeaderMessage :: !CString,+    asapoGenericRequestHeaderStream :: !CString,+    asapoGenericRequestHeaderApiVersion :: !CString+  }++instance Storable AsapoGenericRequestHeader where+  sizeOf _ = (# size struct AsapoGenericRequestHeader)+  alignment _ = (# alignment struct AsapoGenericRequestHeader)+  peek ptr = do+    customData <- peekArray (fromIntegral kNCustomParams) ((# ptr struct AsapoGenericRequestHeader, custom_data) ptr)+    opCode <- (# peek struct AsapoGenericRequestHeader, op_code) ptr+    dataId <- (# peek struct AsapoGenericRequestHeader, data_id) ptr+    dataSize <- (# peek struct AsapoGenericRequestHeader, data_size) ptr+    metaSize <- (# peek struct AsapoGenericRequestHeader, meta_size) ptr+    let message' = (# ptr struct AsapoGenericRequestHeader, message) ptr+    let stream' = (# ptr struct AsapoGenericRequestHeader, stream) ptr+    let apiVersion' = (# ptr struct AsapoGenericRequestHeader, api_version) ptr+    AsapoGenericRequestHeader+      <$> pure opCode+      <*> pure dataId+      <*> pure dataSize+      <*> pure metaSize+      <*> pure customData+      <*> pure message'+      <*> pure stream'+      <*> pure apiVersion'+  poke _ = error "why was AsapoGenericRequestHeader poked? it's supposed to be read-only"+++type AsapoRequestHandlerType = CInt++foreign import capi "asapo/producer_c.h value kTcp" kTcp :: AsapoRequestHandlerType++foreign import capi "asapo/producer_c.h value kFilesystem" kFilesystem :: AsapoRequestHandlerType++type AsapoIngestModeFlags = CInt++foreign import capi "asapo/producer_c.h value kTransferData" kTransferData :: AsapoIngestModeFlags++foreign import capi "asapo/producer_c.h value kTransferMetaDataOnly" kTransferMetaDataOnly :: AsapoIngestModeFlags++foreign import capi "asapo/producer_c.h value kStoreInFilesystem" kStoreInFilesystem :: AsapoIngestModeFlags++foreign import capi "asapo/producer_c.h value kStoreInDatabase" kStoreInDatabase :: AsapoIngestModeFlags++foreign import capi "asapo/producer_c.h value kDefaultIngestMode" kDefaultIngestMode :: AsapoIngestModeFlags++type AsapoMetaIngestOp = CInt++foreign import capi "asapo/producer_c.h value kInsert" kInsert :: AsapoMetaIngestOp++foreign import capi "asapo/producer_c.h value kReplace" kReplace :: AsapoMetaIngestOp++foreign import capi "asapo/producer_c.h value kUpdate" kUpdate :: AsapoMetaIngestOp++type AsapoLogLevel = CInt++foreign import capi "asapo/producer_c.h value None" asapoLogLevelNone :: AsapoLogLevel++foreign import capi "asapo/producer_c.h value Error" asapoLogLevelError :: AsapoLogLevel++foreign import capi "asapo/producer_c.h value Info" asapoLogLevelInfo :: AsapoLogLevel++foreign import capi "asapo/producer_c.h value Debug" asapoLogLevelDebug :: AsapoLogLevel++foreign import capi "asapo/producer_c.h value Warning" asapoLogLevelWarning :: AsapoLogLevel++foreign import capi "asapo/producer_c.h asapo_create_producer"+  asapo_create_producer ::+    -- endpoint+    CString ->+    -- processing threads+    CUChar ->+    AsapoRequestHandlerType ->+    AsapoSourceCredentialsHandle ->+    -- timeout_ms+    Word64 ->+    Ptr AsapoErrorHandle ->+    IO AsapoProducerHandle++foreign import capi "asapo/producer_c.h asapo_producer_get_version_info"+  asapo_producer_get_version_info ::+    AsapoProducerHandle ->+    -- client info+    AsapoStringHandle ->+    -- server info+    AsapoStringHandle ->+    -- supported+    Ptr AsapoBool ->+    Ptr AsapoErrorHandle ->+    IO CInt++foreign import capi "asapo/producer_c.h asapo_producer_get_stream_info"+  asapo_producer_get_stream_info ::+    AsapoProducerHandle ->+    -- stream+    ConstCString ->+    -- timeout ms+    Word64 ->+    Ptr AsapoErrorHandle ->+    IO AsapoStreamInfoHandle++foreign import capi "asapo/producer_c.h asapo_producer_get_stream_meta"+  asapo_producer_get_stream_meta ::+    AsapoProducerHandle ->+    -- stream+    ConstCString ->+    -- timeout_ms+    Word64 ->+    Ptr AsapoErrorHandle ->+    IO AsapoStringHandle++foreign import capi "asapo/producer_c.h asapo_producer_get_beamtime_meta"+  asapo_producer_get_beamtime_meta ::+    AsapoProducerHandle ->+    -- timeout_ms+    Word64 ->+    Ptr AsapoErrorHandle ->+    IO AsapoStringHandle++foreign import capi "asapo/producer_c.h asapo_producer_delete_stream"+  asapo_producer_delete_stream ::+    AsapoProducerHandle ->+    -- stream+    ConstCString ->+    -- timeout_ms+    Word64 ->+    -- delete meta+    AsapoBool ->+    -- error_on_not_exist+    AsapoBool ->+    Ptr AsapoErrorHandle ->+    IO CInt++foreign import capi "asapo/producer_c.h asapo_producer_get_last_stream"+  asapo_producer_get_last_stream ::+    AsapoProducerHandle ->+    -- timeout ms+    Word64 ->+    Ptr AsapoErrorHandle ->+    IO AsapoStreamInfoHandle++foreign import capi "asapo/producer_c.h asapo_create_message_header"+  asapo_create_message_header ::+    -- message ID+    Word64 ->+    -- data size+    Word64 ->+    -- file name+    ConstCString ->+    -- user metadata+    ConstCString ->+    -- dataset substream+    Word64 ->+    -- dataset size+    Word64 ->+    -- auto id+    AsapoBool ->+    IO AsapoMessageHeaderHandle++foreign import capi "asapo/producer_c.h asapo_producer_send"+  asapo_producer_send ::+    AsapoProducerHandle ->+    AsapoMessageHeaderHandle ->+    -- data+    Ptr () ->+    -- ingest mode+    Word64 ->+    -- stream+    ConstCString ->+    FunPtr AsapoRequestCallback ->+    Ptr AsapoErrorHandle ->+    IO CInt++foreign import capi "asapo/producer_c.h asapo_producer_send_file"+  asapo_producer_send_file ::+    AsapoProducerHandle ->+    AsapoMessageHeaderHandle ->+    -- file name+    ConstCString ->+    -- ingest mode+    Word64 ->+    -- stream+    ConstCString ->+    FunPtr AsapoRequestCallback ->+    Ptr AsapoErrorHandle ->+    IO CInt++foreign import capi "asapo/producer_c.h asapo_producer_send_stream_finished_flag"+  asapo_producer_send_stream_finished_flag ::+    AsapoProducerHandle ->+    -- stream+    ConstCString ->+    -- last ID+    Word64 ->+    -- next stream+    ConstCString ->+    FunPtr AsapoRequestCallback ->+    Ptr AsapoErrorHandle ->+    IO CInt++foreign import capi "asapo/producer_c.h asapo_producer_send_beamtime_metadata"+  asapo_producer_send_beamtime_metadata ::+    AsapoProducerHandle ->+    -- metadata+    ConstCString ->+    AsapoMetaIngestOp ->+    -- upsert+    AsapoBool ->+    FunPtr AsapoRequestCallback ->+    Ptr AsapoErrorHandle ->+    IO CInt++foreign import capi "asapo/producer_c.h asapo_producer_send_stream_metadata"+  asapo_producer_send_stream_metadata ::+    AsapoProducerHandle ->+    -- metadata+    ConstCString ->+    AsapoMetaIngestOp ->+    -- upsert+    AsapoBool ->+    -- stream+    ConstCString ->+    FunPtr AsapoRequestCallback ->+    Ptr AsapoErrorHandle ->+    IO CInt++foreign import capi "asapo/producer_c.h asapo_request_callback_payload_get_response"+  asapo_request_callback_payload_get_response :: AsapoRequestCallbackPayloadHandle -> IO AsapoStringHandle++foreign import capi "asapo/producer_c.h asapo_request_callback_payload_get_original_header"+  asapo_request_callback_payload_get_original_header :: AsapoRequestCallbackPayloadHandle -> IO (ConstPtr AsapoGenericRequestHeader)++foreign import capi "asapo/producer_c.h asapo_producer_set_log_level"+  asapo_producer_set_log_level :: AsapoProducerHandle -> AsapoLogLevel -> IO ()++foreign import capi "asapo/producer_c.h asapo_producer_enable_local_log"+  asapo_producer_enable_local_log :: AsapoProducerHandle -> AsapoBool -> IO ()++foreign import capi "asapo/producer_c.h asapo_producer_enable_remote_log"+  asapo_producer_enable_remote_log :: AsapoProducerHandle -> AsapoBool -> IO ()++foreign import capi "asapo/producer_c.h asapo_producer_set_credentials"+  asapo_producer_set_credentials ::+    AsapoProducerHandle ->+    AsapoSourceCredentialsHandle ->+    Ptr AsapoErrorHandle ->+    IO CInt++foreign import capi "asapo/producer_c.h asapo_producer_get_requests_queue_size"+  asapo_producer_get_requests_queue_size :: AsapoProducerHandle -> IO Word64++foreign import capi "asapo/producer_c.h asapo_producer_get_requests_queue_volume_mb"+  asapo_producer_get_requests_queue_volume_mb :: AsapoProducerHandle -> IO Word64++foreign import capi "asapo/producer_c.h asapo_producer_set_requests_queue_limits"+  asapo_producer_set_requests_queue_limits ::+    AsapoProducerHandle ->+    -- size+    Word64 ->+    -- volume+    Word64 ->+    IO ()++foreign import capi "asapo/producer_c.h asapo_producer_wait_requests_finished"+  asapo_producer_wait_requests_finished ::+    AsapoProducerHandle ->+    -- timeout ms+    Word64 ->+    -- error+    Ptr AsapoErrorHandle ->+    IO CInt
+ tests/doctests.hs view
@@ -0,0 +1,15 @@+module Main where++import Build_doctests (flags, module_sources, pkgs)+-- import Data.Foldable (traverse_)+import System.Environment (unsetEnv)+import Test.DocTest (doctest)++-- This is simply copied from https://github.com/ulidtko/cabal-doctest+main :: IO ()+main = do+  -- traverse_ putStrLn args -- optionally print arguments+  unsetEnv "GHC_ENVIRONMENT" -- see 'Notes'; you may not need this+  doctest args+  where+    args = flags ++ pkgs ++ module_sources