-- -- Copyright 2017, 2018 Warlock -- -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License. -- You may obtain a copy of the License at -- -- http://www.apache.org/licenses/LICENSE-2.0 -- -- Unless required by applicable law or agreed to in writing, software -- distributed under the License is distributed on an "AS IS" BASIS, -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -- See the License for the specific language governing permissions and -- limitations under the License. -- -- | This module provides the 'PutS' functor, -- and all functions, which could not be defined using 'PutS' public interface only. module Data.Conduit.Parsers.PutS ( EncodingState (..) , VoidEncodingState (..) , Encoding , encodingWrote , runEncoding , startEncoding , PutS , runPutS , putS , PutM ) where import Control.Monad.Fix import Control.Monad.Trans.State.Strict import Data.Conduit hiding (ConduitM) import Data.String import qualified Data.Text as S (Text) -- | Collects encoding process feedback. class EncodingState s where type EncodingToken s :: * encoded :: EncodingToken s -> s -> s -- | Trivial encoding state. data VoidEncodingState = VoidEncodingState instance EncodingState VoidEncodingState where type EncodingToken VoidEncodingState = () encoded () = id {-# INLINE encoded #-} -- | 'PutS' monad state. data Encoding s m = Encoding { encodingWrote :: !s -- ^ Get the total number of bytes wrote to this point. , runEncoding :: !(m ()) -- ^ Get the 'Producer'. } instance (EncodingState s, Monad m) => EncodingState (Encoding s m) where type EncodingToken (Encoding s m) = (m (), EncodingToken s) encoded (!producer, !bytes_count) !s = Encoding { encodingWrote = encoded bytes_count (encodingWrote s) , runEncoding = runEncoding s >> producer } {-# INLINE encoded #-} -- | Construct 'PutS' initial state. startEncoding :: Applicative m => s -> Encoding s m startEncoding !bytes_wrote_before = Encoding { encodingWrote = bytes_wrote_before , runEncoding = pure () } {-# INLINE startEncoding #-} -- | Wrappers for 'PutM' with inner monad @m@ and result @a@ (usually @()@). newtype PutS s m a = S { runS :: State (Encoding s m) a } deriving instance Monad (PutS s m) deriving instance Functor (PutS s m) deriving instance MonadFix (PutS s m) deriving instance Applicative (PutS s m) instance Monad m => Semigroup (PutS s m ()) where a <> b = a >> b {-# INLINE (<>) #-} -- | A 'ConduitT' with wrappers supposed to a binary or text serialization. type PutM s i o m a = PutS s (ConduitT i o m) a instance (EncodingState s, EncodingToken s ~ (), Monad m) => IsString (PutM s i S.Text m ()) where fromString x = putS $ \ !t -> ((), encoded (yield (fromString x), ()) t) {-# INLINE fromString #-} -- | Run a 'Put' monad, unwrapping all wrappers in a reversible way. -- @'putS' . runPutS = 'id'@ runPutS :: PutS s m a -> Encoding s m -> (a, Encoding s m) runPutS = runState . runS {-# INLINE runPutS #-} -- | Custom 'Put'. -- @putS . 'runPutS' = 'id'@ putS :: (Encoding s m -> (a, Encoding s m)) -> PutS s m a putS = S . state {-# INLINE putS #-}