{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Data.ASN1.Parse
( ParseASN1
, runParseASN1State
, runParseASN1
, onNextContainer
, onNextContainerMaybe
, getNextContainer
, getNextContainerMaybe
, getNext
, getNextMaybe
, hasNext
, getObject
, getMany
) where
import Data.ASN1.Types
import Data.ASN1.Stream
import Control.Monad.State
import Control.Monad.Error
import Control.Applicative (Applicative, (<$>))
newtype ParseASN1 a = P { runP :: ErrorT String (State [ASN1]) a }
deriving (Functor, Applicative, Monad, MonadError String, MonadState [ASN1])
runParseASN1State :: ParseASN1 a -> [ASN1] -> Either String (a,[ASN1])
runParseASN1State f s =
case runState (runErrorT (runP f)) s of
(Left err, _) -> Left err
(Right r, l) -> Right (r,l)
runParseASN1 :: ParseASN1 a -> [ASN1] -> Either String a
runParseASN1 f s =
case runParseASN1State f s of
Left err -> Left err
Right (o, []) -> Right o
Right (_, er) -> throwError ("runParseASN1: remaining state " ++ show er)
getObject :: ASN1Object a => ParseASN1 a
getObject = do
l <- get
case fromASN1 l of
Left err -> throwError err
Right (a,l2) -> put l2 >> return a
getNext :: ParseASN1 ASN1
getNext = do
list <- get
case list of
[] -> throwError "empty"
(h:l) -> put l >> return h
getMany :: ParseASN1 a -> ParseASN1 [a]
getMany getOne = do
next <- hasNext
if next
then liftM2 (:) getOne (getMany getOne)
else return []
getNextMaybe :: (ASN1 -> Maybe a) -> ParseASN1 (Maybe a)
getNextMaybe f = do
list <- get
case list of
[] -> return Nothing
(h:l) -> let r = f h
in do case r of
Nothing -> put list
Just _ -> put l
return r
getNextContainer :: ASN1ConstructionType -> ParseASN1 [ASN1]
getNextContainer ty = do
list <- get
case list of
[] -> throwError "empty"
(h:l) | h == Start ty -> do let (l1, l2) = getConstructedEnd 0 l
put l2 >> return l1
| otherwise -> throwError "not an expected container"
onNextContainer :: ASN1ConstructionType -> ParseASN1 a -> ParseASN1 a
onNextContainer ty f = getNextContainer ty >>= either throwError return . runParseASN1 f
getNextContainerMaybe :: ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe ty = do
list <- get
case list of
[] -> return Nothing
(h:l) | h == Start ty -> do let (l1, l2) = getConstructedEnd 0 l
put l2 >> return (Just l1)
| otherwise -> return Nothing
onNextContainerMaybe :: ASN1ConstructionType -> ParseASN1 a -> ParseASN1 (Maybe a)
onNextContainerMaybe ty f = do
n <- getNextContainerMaybe ty
case n of
Just l -> either throwError (return . Just) $ runParseASN1 f l
Nothing -> return Nothing
hasNext :: ParseASN1 Bool
hasNext = not . null <$> get