ymonad-0.1.0.0: src/YMonad/Protocol/Wayland/Xml.hs
{-# LANGUAGE Arrows #-}
{-# LANGUAGE OverloadedStrings #-}
module YMonad.Protocol.Wayland.Xml (
loadProtocolSpec,
) where
import Data.Map.Strict qualified as M
import Text.XML.HXT.Core
import YMonad.Protocol.Wayland.Types
loadProtocolSpec :: FilePath -> IO (Either String ProtocolSpec)
loadProtocolSpec path = do
specs <- runX (readDocument [withValidate no, withRemoveWS yes] path >>> parseProtocol)
pure $ case specs of
[] -> Left ("No <protocol> element found in: " <> path)
spec : _ -> Right spec
parseProtocol :: IOSArrow XmlTree ProtocolSpec
parseProtocol =
deep (isElem >>> hasName "protocol") >>> proc proto -> do
name <- getAttrValue "name" -< proto
interfaces <- listA (getChildren >>> isElem >>> hasName "interface" >>> parseInterface) -< proto
returnA
-<
ProtocolSpec
{ protocolName = toText name
, protocolInterfaces = M.fromList [(interfaceName iface, iface) | iface <- interfaces]
}
parseInterface :: IOSArrow XmlTree InterfaceSpec
parseInterface = proc iface -> do
name <- getAttrValue "name" -< iface
versionTxt <- getAttrValue "version" -< iface
requests <- listA (getChildren >>> isElem >>> hasName "request" >>> parseRequest) -< iface
events <- listA (getChildren >>> isElem >>> hasName "event" >>> parseEvent) -< iface
let version = parseWord32 1 versionTxt
returnA -< mkInterfaceSpec (toText name) version (assignOpcodes MessageRequest requests) (assignOpcodes MessageEvent events)
parseRequest :: IOSArrow XmlTree MessageSpec
parseRequest = parseMessage MessageRequest
parseEvent :: IOSArrow XmlTree MessageSpec
parseEvent = parseMessage MessageEvent
parseMessage :: MessageRole -> IOSArrow XmlTree MessageSpec
parseMessage role = proc msg -> do
name <- getAttrValue "name" -< msg
args <- listA (getChildren >>> isElem >>> hasName "arg" >>> parseArg) -< msg
returnA
-<
MessageSpec
{ messageSpecName = toText name
, messageSpecOpcode = 0
, messageSpecRole = role
, messageSpecArgs = args
}
parseArg :: IOSArrow XmlTree ArgSpec
parseArg = proc arg -> do
name <- getAttrValue "name" -< arg
typeName <- getAttrValue "type" -< arg
iface <- getAttrValue "interface" -< arg
allowNull <- getAttrValue "allow-null" -< arg
returnA
-<
ArgSpec
{ argSpecName = toText name
, argSpecType = parseArgType (toText typeName) (toMaybeText iface)
, argSpecAllowNull = allowNull == "true"
}
assignOpcodes :: MessageRole -> [MessageSpec] -> [MessageSpec]
assignOpcodes role = zipWith assign [0 :: Word16 ..]
where
assign opcode spec = spec {messageSpecOpcode = opcode, messageSpecRole = role}
parseArgType :: Text -> Maybe Text -> ArgType
parseArgType typeName iface = case typeName of
"int" -> ArgInt
"uint" -> ArgUint
"string" -> ArgString
"object" -> ArgObject iface
"new_id" -> ArgNewId iface
_ -> ArgString
parseWord32 :: Word32 -> String -> Word32
parseWord32 fallback raw = fromMaybe fallback (readMaybe raw)
toMaybeText :: String -> Maybe Text
toMaybeText "" = Nothing
toMaybeText raw = Just (toText raw)