content: -- |
  --
  -- Module      : Raaz.Core.Parse
  -- Description : Applicative parsing from buffers.
  -- Copyright   : (c) Piyush P Kurur, 2019
  -- License     : Apache-2.0 OR BSD-3-Clause
  -- Maintainer  : Piyush P Kurur <ppk@iitpkd.ac.in>
  -- Stability   : experimental
  --
  
  module Raaz.Core.Parse
         ( Parser, parseWidth, parseError, runParser
         , parse, parseStorable
         , parseVector, parseStorableVector
         , parseByteString
         , skip
         ) where
  
  import           Data.ByteString           (ByteString)
  import           Data.Vector.Generic       (Vector)
  import           Foreign.Storable          (Storable, peek)
  import           System.IO.Unsafe          (unsafePerformIO)
  
  import           Raaz.Core.Parse.Unsafe
  import           Raaz.Core.Prelude
  import           Raaz.Core.Types.Endian
  import           Raaz.Core.Types.Pointer
  import           Raaz.Core.Util.ByteString (createFrom, length, withByteString)
  
  -- | Skip over some data.
  skip :: LengthUnit u => u -> Parser ()
  skip = flip unsafeMakeParser doNothing
    where doNothing = const $ return ()
  
  -- | A parser that fails with a given error message.
  parseError  :: String -> Parser a
  parseError msg = unsafeMakeParser (0 :: BYTES Int) $ \ _ -> fail msg
  
  -- | Runs a parser on a byte string. It returns `Nothing` if the byte string is smaller than
  -- what the parser would consume.
  runParser :: Parser a -> ByteString -> Maybe a
  runParser pr bs
    | length bs < parseWidth pr = Nothing
    | otherwise                 = Just $ unsafePerformIO $ withByteString bs $ unsafeRunParser pr
  
  -- | The primary purpose of this function is to satisfy type checkers.
  parserToProxy   :: Parser a -> Proxy a
  parserToProxy _ = Proxy
  
  -- | Parses a value which is an instance of Storable. Beware that this
  -- parser expects that the value is stored in machine endian. Mostly
  -- it is useful in defining the `peek` function in a complicated
  -- `Storable` instance.
  parseStorable :: Storable a => Parser a
  parseStorable = pa
    where pa = unsafeMakeParser (sizeOf $ parserToProxy pa) (peek . castPointer)
  
  -- | Parse a crypto value. Endian safety is take into account
  -- here. This is what you would need when you parse packets from an
  -- external source. You can also use this to define the `load`
  -- function in a complicated `EndianStore` instance.
  parse :: EndianStore a => Parser a
  parse = pa
    where pa = unsafeMakeParser (sizeOf $ parserToProxy pa) (load . castPointer)
  
  -- | Parses a strict bytestring of a given length.
  parseByteString :: LengthUnit l => l -> Parser ByteString
  parseByteString l = unsafeMakeParser l $ createFrom l
  
  -- | Similar to `parseVector` but parses according to the host
  -- endian. This function is essentially used to define storable
  -- instances of complicated data. It is unlikely to be of use when
  -- parsing externally serialised data as one would want to keep track
  -- of the endianness of the data.
  parseStorableVector :: (Storable a, Vector v a) => Int -> Parser (v a)
  parseStorableVector n | n < 0      = parseError $ "parseStorableVector on " ++ show n
                        | otherwise  = unsafeParseStorableVector n
  
  -- | Parses a vector of elements. It takes care of the correct endian
  -- conversion. This is the function to use while parsing external
  -- data.
  parseVector :: (EndianStore a, Vector v a) => Int -> Parser (v a)
  parseVector n | n < 0      = parseError $ "parseVector on " ++ show n
                | otherwise  = unsafeParseVector n
  