-- | An abstract transport layer with implementations for @Udp@ and @Tcp@ transport. module Sound.Osc.Transport.Fd where import Control.Exception {- base -} import Data.List {- base -} import Data.Maybe {- base -} import Sound.Osc.Datum {- hosc -} import Sound.Osc.Packet {- hosc -} import qualified Sound.Osc.Wait as Wait {- hosc -} -- | Abstract over the underlying transport protocol. class Transport t where -- | Encode and send an Osc packet. sendPacket :: t -> PacketOf Message -> IO () -- | Receive and decode an Osc packet. recvPacket :: t -> IO (PacketOf Message) -- | Receive and either decode an Osc packet. recvPacketOr :: t -> IO (Either String Packet) -- | Close an existing connection. close :: t -> IO () -- | Bracket Osc communication. withTransport :: Transport t => IO t -> (t -> IO a) -> IO a withTransport u = bracket u close -- * Send -- | 'sendPacket' of 'Packet_Message'. sendMessage :: Transport t => t -> Message -> IO () sendMessage t = sendPacket t . Packet_Message -- | 'sendPacket' of 'Packet_Bundle'. sendBundle :: Transport t => t -> BundleOf Message -> IO () sendBundle t = sendPacket t . Packet_Bundle -- * Receive -- | Variant of 'recvPacket' that runs 'packet_to_bundle'. recvBundle :: (Transport t) => t -> IO (BundleOf Message) recvBundle = fmap packet_to_bundle . recvPacket -- | Variant of 'recvPacket' that runs 'packet_to_message'. recvMessage :: (Transport t) => t -> IO (Maybe Message) recvMessage = fmap packet_to_message . recvPacket -- | Erroring variant. recvMessage_err :: (Transport t) => t -> IO Message recvMessage_err = fmap (fromMaybe (error "recvMessage")) . recvMessage -- | Variant of 'recvPacket' that runs 'packetMessages'. recvMessages :: (Transport t) => t -> IO [Message] recvMessages = fmap packetMessages . recvPacket -- * Wait {- | Wait for a 'Packet' where the supplied predicate is 'True', discarding intervening packets. -} waitUntil :: (Transport t) => t -> (PacketOf Message -> Bool) -> IO (PacketOf Message) waitUntil t f = Wait.untilPredicate f (recvPacket t) {- | Wait for a 'Packet' where the supplied function does not give 'Nothing', discarding intervening packets. -} waitFor :: (Transport t) => t -> (PacketOf Message -> Maybe a) -> IO a waitFor t f = Wait.untilMaybe f (recvPacket t) -- | 'waitUntil' 'packet_is_immediate'. waitImmediate :: Transport t => t -> IO (PacketOf Message) waitImmediate t = waitUntil t packet_is_immediate {- | 'waitFor' 'packet_to_message', ie. an incoming 'Message' or immediate mode 'Bundle' with one element. -} waitMessage :: Transport t => t -> IO Message waitMessage t = waitFor t packet_to_message {- | A 'waitFor' for variant using 'packet_has_address' to match on the 'Address_Pattern' of incoming 'Packets'. -} waitAddress :: Transport t => t -> Address_Pattern -> IO (PacketOf Message) waitAddress t s = let f o = if packet_has_address s o then Just o else Nothing in waitFor t f -- | Variant on 'waitAddress' that returns matching 'Message'. waitReply :: Transport t => t -> Address_Pattern -> IO Message waitReply t s = let f = fromMaybe (error "waitReply: message not located?") . find (message_has_address s) . packetMessages in fmap f (waitAddress t s) -- | Variant of 'waitReply' that runs 'messageDatum'. waitDatum :: Transport t => t -> Address_Pattern -> IO [Datum] waitDatum t = fmap messageDatum . waitReply t