module Network.DBus.Signature
( Signature
, SignatureElem(..)
, serializeSignature
, unserializeSignature
) where
import Data.Char (chr, ord)
import Data.Serialize.Get
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import Control.Monad
import Control.Applicative ((<$>))
data SignatureElem =
SigByte
| SigBool
| SigInt16
| SigUInt16
| SigInt32
| SigUInt32
| SigInt64
| SigUInt64
| SigDouble
| SigString
| SigObjectPath
| SigSignature
| SigArray SignatureElem
| SigStruct [SignatureElem]
| SigVariant
| SigDict SignatureElem SignatureElem
| SigUnixFD
deriving (Show,Eq)
type Signature = [SignatureElem]
marshallString :: String -> ByteString
marshallString = B.pack . map (fromIntegral . ord)
marshallSignatureElem :: SignatureElem -> ByteString
marshallSignatureElem SigByte = marshallString "y"
marshallSignatureElem SigBool = marshallString "b"
marshallSignatureElem SigInt16 = marshallString "n"
marshallSignatureElem SigUInt16 = marshallString "q"
marshallSignatureElem SigInt32 = marshallString "i"
marshallSignatureElem SigUInt32 = marshallString "u"
marshallSignatureElem SigInt64 = marshallString "x"
marshallSignatureElem SigUInt64 = marshallString "t"
marshallSignatureElem SigDouble = marshallString "d"
marshallSignatureElem SigString = marshallString "s"
marshallSignatureElem SigObjectPath = marshallString "o"
marshallSignatureElem SigSignature = marshallString "g"
marshallSignatureElem SigVariant = marshallString "v"
marshallSignatureElem SigUnixFD = marshallString "h"
marshallSignatureElem (SigArray t) = B.concat [marshallString "a", marshallSignatureElem t]
marshallSignatureElem (SigStruct l) = B.concat ([marshallString "("] ++ map marshallSignatureElem l ++ [marshallString ")"])
marshallSignatureElem (SigDict k v) = B.concat [marshallString "{", marshallSignatureElem k, marshallSignatureElem v, marshallString "}"]
serializeSignature :: Signature -> ByteString
serializeSignature elements = B.concat $ map marshallSignatureElem elements
data SigStop = StopStruct | StopDict deriving (Eq)
unserializeSignature :: ByteString -> Either String Signature
unserializeSignature = runGet loop where
loop :: Get Signature
loop = do
o <- getOneNoCollection
r <- remaining
if r > 0 then liftM (o :) loop else return [o]
getOneNoCollection = getOne >>= \o -> case o of
Left _ -> error "parsing error, end of collection not in a collection"
Right z -> return z
getOne :: Get (Either SigStop SignatureElem)
getOne = do
t <- chr . fromIntegral <$> getWord8
case t of
')' -> return $ Left StopStruct
'}' -> return $ Left StopDict
'y' -> return $ Right SigByte
'b' -> return $ Right SigBool
'n' -> return $ Right SigInt16
'q' -> return $ Right SigUInt16
'i' -> return $ Right SigInt32
'u' -> return $ Right SigUInt32
'x' -> return $ Right SigInt64
't' -> return $ Right SigUInt64
'd' -> return $ Right SigDouble
's' -> return $ Right SigString
'o' -> return $ Right SigObjectPath
'g' -> return $ Right SigSignature
'a' -> Right <$> getArray
'(' -> Right <$> getStruct
'v' -> return $ Right SigVariant
'{' -> Right <$> getDict
'h' -> return $ Right SigUnixFD
_ -> error ("unknown signature element: " ++ (show $ ord t))
getArray = SigArray <$> getOneNoCollection
getDict = do
l <- loopTill StopDict
case l of
[k,v] -> return $ SigDict k v
_ -> error "dictionary wrong size"
getStruct = do
l <- loopTill StopStruct
case l of
[] -> error "structure empty"
_ -> return $ SigStruct l
loopTill stop = do
o <- getOne
case o of
Left s -> if stop == s then return [] else error "collection not terminated properly"
Right z -> liftM (z :) (loopTill stop)