#line 31 "src/types.anansi" #line 30 "src/introduction.anansi" -- Copyright (C) 2009-2010 John Millikin -- -- This program is free software: you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation, either version 3 of the License, or -- any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with this program. If not, see . #line 32 "src/types.anansi" #line 52 "src/introduction.anansi" {-# LANGUAGE OverloadedStrings #-} #line 33 "src/types.anansi" #line 372 "src/types.anansi" {-# LANGUAGE TypeSynonymInstances #-} #line 34 "src/types.anansi" module DBus.Types.Internal where #line 56 "src/introduction.anansi" import Data.Text.Lazy (Text) import qualified Data.Text.Lazy as TL #line 36 "src/types.anansi" #line 325 "src/types.anansi" import Data.Word (Word8, Word16, Word32, Word64) import Data.Int (Int16, Int32, Int64) #line 359 "src/types.anansi" import qualified Data.Text as T #line 433 "src/types.anansi" import Data.Ord (comparing) #line 454 "src/types.anansi" import Data.Text.Encoding (decodeUtf8) #line 513 "src/types.anansi" import qualified Data.ByteString.Unsafe as B import qualified Foreign as F import System.IO.Unsafe (unsafePerformIO) #line 684 "src/types.anansi" import Data.Text.Lazy.Encoding (encodeUtf8) #line 697 "src/types.anansi" import DBus.Util (mkUnsafe) import qualified Data.String as String #line 772 "src/types.anansi" import Text.ParserCombinators.Parsec ((<|>)) import qualified Text.ParserCombinators.Parsec as P import DBus.Util (checkLength, parseMaybe) #line 978 "src/types.anansi" import Data.List (intercalate) #line 995 "src/types.anansi" import Control.Monad (unless) #line 1017 "src/types.anansi" import Control.Arrow ((***)) import qualified Data.Map as Map #line 1029 "src/types.anansi" import Control.Monad (forM) #line 37 "src/types.anansi" import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as B8 import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Char8 as BL8 #line 52 "src/types.anansi" data Type = DBusBoolean | DBusByte | DBusInt16 | DBusInt32 | DBusInt64 | DBusWord16 | DBusWord32 | DBusWord64 | DBusDouble | DBusString | DBusSignature | DBusObjectPath | DBusVariant | DBusArray Type | DBusDictionary Type Type | DBusStructure [Type] deriving (Show, Eq) #line 73 "src/types.anansi" #line 27 "src/api-docs.anansi" -- | \"Atomic\" types are any which can't contain any other types. Only -- atomic types may be used as dictionary keys. #line 74 "src/types.anansi" isAtomicType :: Type -> Bool isAtomicType DBusBoolean = True isAtomicType DBusByte = True isAtomicType DBusInt16 = True isAtomicType DBusInt32 = True isAtomicType DBusInt64 = True isAtomicType DBusWord16 = True isAtomicType DBusWord32 = True isAtomicType DBusWord64 = True isAtomicType DBusDouble = True isAtomicType DBusString = True isAtomicType DBusSignature = True isAtomicType DBusObjectPath = True isAtomicType _ = False #line 94 "src/types.anansi" #line 32 "src/api-docs.anansi" -- | Every type has an associated type code; a textual representation of -- the type, useful for debugging. #line 95 "src/types.anansi" typeCode :: Type -> Text #line 458 "src/types.anansi" typeCode t = TL.fromChunks [decodeUtf8 $ typeCodeB t] typeCodeB :: Type -> B.ByteString typeCodeB DBusBoolean = "b" typeCodeB DBusByte = "y" typeCodeB DBusInt16 = "n" typeCodeB DBusInt32 = "i" typeCodeB DBusInt64 = "x" typeCodeB DBusWord16 = "q" typeCodeB DBusWord32 = "u" typeCodeB DBusWord64 = "t" typeCodeB DBusDouble = "d" typeCodeB DBusString = "s" typeCodeB DBusSignature = "g" typeCodeB DBusObjectPath = "o" typeCodeB DBusVariant = "v" #line 480 "src/types.anansi" typeCodeB (DBusArray t) = B8.cons 'a' $ typeCodeB t #line 487 "src/types.anansi" typeCodeB (DBusDictionary k v) = B.concat ["a{", typeCodeB k, typeCodeB v, "}"] #line 495 "src/types.anansi" typeCodeB (DBusStructure ts) = B.concat $ ["("] ++ map typeCodeB ts ++ [")"] #line 150 "src/types.anansi" #line 37 "src/api-docs.anansi" -- | 'Variant's may contain any other built-in D-Bus value. Besides -- representing native @VARIANT@ values, they allow type-safe storage and -- deconstruction of heterogeneous collections. #line 151 "src/types.anansi" data Variant = VarBoxBool Bool | VarBoxWord8 Word8 | VarBoxInt16 Int16 | VarBoxInt32 Int32 | VarBoxInt64 Int64 | VarBoxWord16 Word16 | VarBoxWord32 Word32 | VarBoxWord64 Word64 | VarBoxDouble Double | VarBoxString Text | VarBoxSignature Signature | VarBoxObjectPath ObjectPath | VarBoxVariant Variant | VarBoxArray Array | VarBoxDictionary Dictionary | VarBoxStructure Structure deriving (Eq) class Variable a where toVariant :: a -> Variant fromVariant :: Variant -> Maybe a #line 186 "src/types.anansi" instance Show Variant where showsPrec d var = showParen (d > 10) full where full = s "Variant " . shows code . s " " . valueStr code = typeCode $ variantType var s = showString valueStr = showsPrecVar 11 var showsPrecVar :: Int -> Variant -> ShowS showsPrecVar d var = case var of (VarBoxBool x) -> showsPrec d x (VarBoxWord8 x) -> showsPrec d x (VarBoxInt16 x) -> showsPrec d x (VarBoxInt32 x) -> showsPrec d x (VarBoxInt64 x) -> showsPrec d x (VarBoxWord16 x) -> showsPrec d x (VarBoxWord32 x) -> showsPrec d x (VarBoxWord64 x) -> showsPrec d x (VarBoxDouble x) -> showsPrec d x (VarBoxString x) -> showsPrec d x (VarBoxSignature x) -> showsPrec d x (VarBoxObjectPath x) -> showsPrec d x (VarBoxVariant x) -> showsPrec d x (VarBoxArray x) -> showsPrec d x (VarBoxDictionary x) -> showsPrec d x (VarBoxStructure x) -> showsPrec d x #line 218 "src/types.anansi" #line 43 "src/api-docs.anansi" -- | Every variant is strongly-typed; that is, the type of its contained -- value is known at all times. This function retrieves that type, so that -- the correct cast can be used to retrieve the value. #line 219 "src/types.anansi" variantType :: Variant -> Type variantType var = case var of (VarBoxBool _) -> DBusBoolean (VarBoxWord8 _) -> DBusByte (VarBoxInt16 _) -> DBusInt16 (VarBoxInt32 _) -> DBusInt32 (VarBoxInt64 _) -> DBusInt64 (VarBoxWord16 _) -> DBusWord16 (VarBoxWord32 _) -> DBusWord32 (VarBoxWord64 _) -> DBusWord64 (VarBoxDouble _) -> DBusDouble (VarBoxString _) -> DBusString (VarBoxSignature _) -> DBusSignature (VarBoxObjectPath _) -> DBusObjectPath (VarBoxVariant _) -> DBusVariant (VarBoxArray x) -> DBusArray (arrayType x) (VarBoxDictionary x) -> let keyT = dictionaryKeyType x valueT = dictionaryValueType x in DBusDictionary keyT valueT (VarBoxStructure x) -> let Structure items = x in DBusStructure (map variantType items) variantSignature :: Variant -> Maybe Signature variantSignature = mkBytesSignature . typeCodeB . variantType #line 255 "src/types.anansi" #define INSTANCE_VARIABLE(TYPE) \ instance Variable TYPE where \ { toVariant = VarBox/**/TYPE \ ; fromVariant (VarBox/**/TYPE x) = Just x \ ; fromVariant _ = Nothing \ } #line 266 "src/types.anansi" INSTANCE_VARIABLE(Variant) #line 330 "src/types.anansi" INSTANCE_VARIABLE(Bool) INSTANCE_VARIABLE(Word8) INSTANCE_VARIABLE(Int16) INSTANCE_VARIABLE(Int32) INSTANCE_VARIABLE(Int64) INSTANCE_VARIABLE(Word16) INSTANCE_VARIABLE(Word32) INSTANCE_VARIABLE(Word64) INSTANCE_VARIABLE(Double) #line 348 "src/types.anansi" instance Variable TL.Text where toVariant = VarBoxString fromVariant (VarBoxString x) = Just x fromVariant _ = Nothing #line 363 "src/types.anansi" instance Variable T.Text where toVariant = toVariant . TL.fromChunks . (:[]) fromVariant = fmap (T.concat . TL.toChunks) . fromVariant #line 376 "src/types.anansi" instance Variable String where toVariant = toVariant . TL.pack fromVariant = fmap TL.unpack . fromVariant #line 409 "src/types.anansi" INSTANCE_VARIABLE(Signature) data Signature = Signature { signatureTypes :: [Type] } deriving (Eq) instance Show Signature where showsPrec d x = showParen (d > 10) $ showString "Signature " . shows (strSignature x) #line 422 "src/types.anansi" bytesSignature :: Signature -> B.ByteString bytesSignature (Signature ts) = B.concat $ map typeCodeB ts strSignature :: Signature -> Text strSignature (Signature ts) = TL.concat $ map typeCode ts #line 437 "src/types.anansi" instance Ord Signature where compare = comparing strSignature #line 529 "src/types.anansi" mkBytesSignature :: B.ByteString -> Maybe Signature mkBytesSignature = unsafePerformIO . flip B.unsafeUseAsCStringLen io where #line 542 "src/types.anansi" parseAtom c yes no = case c of 0x62 -> yes DBusBoolean 0x79 -> yes DBusByte 0x6E -> yes DBusInt16 0x69 -> yes DBusInt32 0x78 -> yes DBusInt64 0x71 -> yes DBusWord16 0x75 -> yes DBusWord32 0x74 -> yes DBusWord64 0x64 -> yes DBusDouble 0x73 -> yes DBusString 0x67 -> yes DBusSignature 0x6F -> yes DBusObjectPath _ -> no fast c = parseAtom c (\t -> Just (Signature [t])) $ case c of 0x76 -> Just (Signature [DBusVariant]) _ -> Nothing #line 532 "src/types.anansi" #line 563 "src/types.anansi" slow :: F.Ptr Word8 -> Int -> IO (Maybe Signature) slow buf len = loop [] 0 where loop acc ii | ii >= len = return . Just . Signature $ reverse acc loop acc ii = do c <- F.peekElemOff buf ii let next t = loop (t : acc) (ii + 1) parseAtom c next $ case c of 0x76 -> next DBusVariant -- '(' 0x28 -> do mt <- structure buf len (ii + 1) case mt of Just (ii', t) -> loop (t : acc) ii' Nothing -> return Nothing -- 'a' 0x61 -> do mt <- array buf len (ii + 1) case mt of Just (ii', t) -> loop (t : acc) ii' Nothing -> return Nothing _ -> return Nothing #line 590 "src/types.anansi" structure :: F.Ptr Word8 -> Int -> Int -> IO (Maybe (Int, Type)) structure buf len = loop [] where loop _ ii | ii >= len = return Nothing loop acc ii = do c <- F.peekElemOff buf ii let next t = loop (t : acc) (ii + 1) parseAtom c next $ case c of 0x76 -> next DBusVariant -- '(' 0x28 -> do mt <- structure buf len (ii + 1) case mt of Just (ii', t) -> loop (t : acc) ii' Nothing -> return Nothing -- ')' 0x29 -> return $ Just $ (ii + 1, DBusStructure (reverse acc)) -- 'a' 0x61 -> do mt <- array buf len (ii + 1) case mt of Just (ii', t) -> loop (t : acc) ii' Nothing -> return Nothing _ -> return Nothing #line 620 "src/types.anansi" array :: F.Ptr Word8 -> Int -> Int -> IO (Maybe (Int, Type)) array _ len ii | ii >= len = return Nothing array buf len ii = do c <- F.peekElemOff buf ii let next t = return $ Just (ii + 1, DBusArray t) parseAtom c next $ case c of 0x76 -> next DBusVariant -- '(' 0x28 -> do mt <- structure buf len (ii + 1) case mt of Just (ii', t) -> return $ Just (ii', DBusArray t) Nothing -> return Nothing -- '{' 0x7B -> dict buf len (ii + 1) -- 'a' 0x61 -> do mt <- array buf len (ii + 1) case mt of Just (ii', t) -> return $ Just (ii', DBusArray t) Nothing -> return Nothing _ -> return Nothing #line 649 "src/types.anansi" dict :: F.Ptr Word8 -> Int -> Int -> IO (Maybe (Int, Type)) dict _ len ii | ii + 1 >= len = return Nothing dict buf len ii = do c1 <- F.peekElemOff buf ii c2 <- F.peekElemOff buf (ii + 1) let mt1 = parseAtom c1 Just Nothing let next t = return $ Just (ii + 2, t) mt2 <- parseAtom c2 next $ case c2 of 0x76 -> next DBusVariant -- '(' 0x28 -> structure buf len (ii + 2) -- 'a' 0x61 -> array buf len (ii + 2) _ -> return Nothing case mt2 of Nothing -> return Nothing Just (ii', t2) -> if ii' >= len then return Nothing else do c3 <- F.peekElemOff buf ii' return $ do if c3 == 0x7D then Just () else Nothing t1 <- mt1 Just (ii' + 1, DBusDictionary t1 t2) #line 533 "src/types.anansi" io (cstr, len) = case len of 0 -> return $ Just $ Signature [] 1 -> fmap fast $ F.peek cstr _ | len <= 255 -> slow (F.castPtr cstr) len _ -> return Nothing #line 688 "src/types.anansi" mkSignature :: Text -> Maybe Signature mkSignature = mkBytesSignature . B.concat . BL.toChunks . encodeUtf8 #line 702 "src/types.anansi" mkSignature_ :: Text -> Signature mkSignature_ = mkUnsafe "signature" mkSignature instance String.IsString Signature where fromString = mkUnsafe "signature" mkBytesSignature . BL8.pack #line 724 "src/types.anansi" maybeValidType :: Type -> Maybe () maybeValidType t = if B.length (typeCodeB t) > 255 then Nothing else Just () #line 745 "src/types.anansi" INSTANCE_VARIABLE(ObjectPath) newtype ObjectPath = ObjectPath { strObjectPath :: Text } deriving (Eq, Ord) instance Show ObjectPath where showsPrec d (ObjectPath x) = showParen (d > 10) $ showString "ObjectPath " . shows x instance String.IsString ObjectPath where fromString = mkObjectPath_ . TL.pack #line 778 "src/types.anansi" mkObjectPath :: Text -> Maybe ObjectPath mkObjectPath s = parseMaybe path' (TL.unpack s) where c = P.oneOf $ ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ "_" path = P.char '/' >>= P.optional . P.sepBy (P.many1 c) . P.char path' = path >> P.eof >> return (ObjectPath s) mkObjectPath_ :: Text -> ObjectPath mkObjectPath_ = mkUnsafe "object path" mkObjectPath #line 822 "src/types.anansi" INSTANCE_VARIABLE(Array) data Array = VariantArray Type [Variant] | ByteArray BL.ByteString deriving (Eq) #line 49 "src/api-docs.anansi" -- | This is the type contained within the array, not the type of the array -- itself. #line 829 "src/types.anansi" arrayType :: Array -> Type arrayType (VariantArray t _) = t arrayType (ByteArray _) = DBusByte arrayItems :: Array -> [Variant] arrayItems (VariantArray _ xs) = xs arrayItems (ByteArray xs) = map toVariant $ BL.unpack xs #line 849 "src/types.anansi" instance Show Array where showsPrec d array = showParen (d > 10) $ s "Array " . showSig . s " [" . s valueString . s "]" where s = showString showSig = shows . typeCode . arrayType $ array showVar var = showsPrecVar 0 var "" valueString = intercalate ", " $ map showVar $ arrayItems array #line 863 "src/types.anansi" arrayFromItems :: Type -> [Variant] -> Maybe Array arrayFromItems DBusByte vs = fmap (ByteArray . BL.pack) (mapM fromVariant vs) arrayFromItems t vs = do maybeValidType t if all (\x -> variantType x == t) vs then Just $ VariantArray t vs else Nothing #line 877 "src/types.anansi" toArray :: Variable a => Type -> [a] -> Maybe Array toArray t = arrayFromItems t . map toVariant fromArray :: Variable a => Array -> Maybe [a] fromArray = mapM fromVariant . arrayItems #line 894 "src/types.anansi" arrayToBytes :: Array -> Maybe BL.ByteString arrayToBytes (ByteArray x) = Just x arrayToBytes _ = Nothing arrayFromBytes :: BL.ByteString -> Array arrayFromBytes = ByteArray #line 911 "src/types.anansi" instance Variable BL.ByteString where toVariant = toVariant . arrayFromBytes fromVariant x = fromVariant x >>= arrayToBytes #line 917 "src/types.anansi" instance Variable B.ByteString where toVariant x = toVariant . arrayFromBytes $ BL.fromChunks [x] fromVariant = fmap (B.concat . BL.toChunks) . fromVariant #line 956 "src/types.anansi" INSTANCE_VARIABLE(Dictionary) data Dictionary = Dictionary { dictionaryKeyType :: Type , dictionaryValueType :: Type , dictionaryItems :: [(Variant, Variant)] } deriving (Eq) #line 982 "src/types.anansi" instance Show Dictionary where showsPrec d (Dictionary kt vt pairs) = showParen (d > 10) $ s "Dictionary " . showSig . s " {" . s valueString . s "}" where s = showString showSig = shows $ TL.append (typeCode kt) (typeCode vt) valueString = intercalate ", " $ map showPair pairs showPair (k, v) = (showsPrecVar 0 k . showString " -> " . showsPrecVar 0 v) "" #line 999 "src/types.anansi" dictionaryFromItems :: Type -> Type -> [(Variant, Variant)] -> Maybe Dictionary dictionaryFromItems kt vt pairs = do unless (isAtomicType kt) Nothing maybeValidType kt maybeValidType vt let sameType (k, v) = variantType k == kt && variantType v == vt if all sameType pairs then Just $ Dictionary kt vt pairs else Nothing #line 1022 "src/types.anansi" toDictionary :: (Variable a, Variable b) => Type -> Type -> Map.Map a b -> Maybe Dictionary toDictionary kt vt = dictionaryFromItems kt vt . pairs where pairs = map (toVariant *** toVariant) . Map.toList #line 1033 "src/types.anansi" fromDictionary :: (Variable a, Ord a, Variable b) => Dictionary -> Maybe (Map.Map a b) fromDictionary (Dictionary _ _ vs) = do pairs <- forM vs $ \(k, v) -> do k' <- fromVariant k v' <- fromVariant v return (k', v') return $ Map.fromList pairs #line 1093 "src/types.anansi" dictionaryToArray :: Dictionary -> Array dictionaryToArray (Dictionary kt vt items) = array where Just array = toArray itemType structs itemType = DBusStructure [kt, vt] structs = [Structure [k, v] | (k, v) <- items] #line 1101 "src/types.anansi" arrayToDictionary :: Array -> Maybe Dictionary arrayToDictionary array = do let toPair x = do struct <- fromVariant x case struct of Structure [k, v] -> Just (k, v) _ -> Nothing (kt, vt) <- case arrayType array of DBusStructure [kt, vt] -> Just (kt, vt) _ -> Nothing pairs <- mapM toPair $ arrayItems array dictionaryFromItems kt vt pairs #line 1126 "src/types.anansi" INSTANCE_VARIABLE(Structure) data Structure = Structure [Variant] deriving (Show, Eq) #line 1153 "src/types.anansi" #define NAME_TYPE(TYPE, NAME) \ newtype TYPE = TYPE {str/**/TYPE :: Text} \ deriving (Eq, Ord); \ \ instance Show TYPE where \ { showsPrec d (TYPE x) = showParen (d > 10) $ \ showString "TYPE " . shows x \ }; \ \ instance String.IsString TYPE where \ { fromString = mk/**/TYPE/**/_ . TL.pack }; \ \ instance Variable TYPE where \ { toVariant = toVariant . str/**/TYPE \ ; fromVariant = (mk/**/TYPE =<<) . fromVariant }; \ \ mk/**/TYPE/**/_ :: Text -> TYPE; \ mk/**/TYPE/**/_ = mkUnsafe NAME mk/**/TYPE #line 1190 "src/types.anansi" NAME_TYPE(BusName, "bus name") mkBusName :: Text -> Maybe BusName mkBusName s = checkLength 255 (TL.unpack s) >>= parseMaybe parser where c = ['a'..'z'] ++ ['A'..'Z'] ++ "_-" c' = c ++ ['0'..'9'] parser = (unique <|> wellKnown) >> P.eof >> return (BusName s) unique = P.char ':' >> elems c' wellKnown = elems c elems start = elem' start >> P.many1 (P.char '.' >> elem' start) elem' start = P.oneOf start >> P.many (P.oneOf c') #line 1245 "src/types.anansi" NAME_TYPE(InterfaceName, "interface name") mkInterfaceName :: Text -> Maybe InterfaceName mkInterfaceName s = checkLength 255 (TL.unpack s) >>= parseMaybe parser where c = ['a'..'z'] ++ ['A'..'Z'] ++ "_" c' = c ++ ['0'..'9'] element = P.oneOf c >> P.many (P.oneOf c') name = element >> P.many1 (P.char '.' >> element) parser = name >> P.eof >> return (InterfaceName s) #line 1291 "src/types.anansi" NAME_TYPE(ErrorName, "error name") mkErrorName :: Text -> Maybe ErrorName mkErrorName = fmap (ErrorName . strInterfaceName) . mkInterfaceName #line 1323 "src/types.anansi" NAME_TYPE(MemberName, "member name") mkMemberName :: Text -> Maybe MemberName mkMemberName s = checkLength 255 (TL.unpack s) >>= parseMaybe parser where c = ['a'..'z'] ++ ['A'..'Z'] ++ "_" c' = c ++ ['0'..'9'] name = P.oneOf c >> P.many (P.oneOf c') parser = name >> P.eof >> return (MemberName s)