packages feed

roshask-0.2: src/executable/MD5.hs

{-# LANGUAGE OverloadedStrings #-}
module MD5 (msgMD5, srvMD5) where
import Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.Char8 as B
import Data.Digest.Pure.MD5 (md5)
import Data.ByteString.Lazy (fromChunks)
import Data.Map ((!))
import qualified Data.Map as M
import Parse (simpleFieldAssoc)
import Types
import Analysis
import Control.Applicative ((<$>))

tMap :: M.Map MsgType ByteString
tMap = M.fromList simpleFieldAssoc

typeText :: MsgType -> MsgInfo ByteString
typeText (RFixedArray n t) = 
  case M.lookup t tMap of
    Just t' -> return $ B.concat [t', "[", B.pack . show $ n, "]"]
    Nothing -> typeText t
typeText (RVarArray t) =
  case M.lookup t tMap of
    Just t' -> return $ B.append t' "[]"
    Nothing -> typeText t
typeText (RUserType b) = fmap B.pack (getMsg b >>= msgMD5 . snd)
typeText t = return $ tMap ! t

md5sum :: ByteString -> String
md5sum = show . md5 . fromChunks . (:[])

-- The "MD5 text" of a message is the .msg text with
-- * comments removed
-- * whitespace removed
-- * package names of dependencies removed
-- * constants reordered ahead of other declarations
-- from http://www.ros.org/wiki/ROS/Technical%20Overview
--
-- For user defined types, rather than recording a field as "type
-- identifier", we record "md5 identifier".
msgMD5 :: Msg -> MsgInfo String
msgMD5 m = md5sum <$> msgMD5Text m

-- | Generate the text the md5 is computed from
msgMD5Text :: Msg -> MsgInfo ByteString
msgMD5Text m = let cs = map constantText (constants m)
               in withMsg m $
                do fs <- mapM fieldText (fields m)
                   return $ B.intercalate "\n" (cs ++ fs)
  where constantText (MsgConst _ t v n) = B.concat [tMap ! t, " ", n, "=", v]
        fieldText (MsgField _ t n) = do t' <- typeText t
                                        return $ B.concat [t', " ", n]

-- | The MD5 for the service is generated by concatenating the MD5 text
-- | for the request and response and then calculating the MD5 sum.
-- | I have not found any ROS specification for this, but this is how
-- | roslib does it (see _compute_hash in
-- | http://docs.ros.org/api/roslib/html/python/roslib.gentools-pysrc.html).
srvMD5 :: Srv -> MsgInfo String
srvMD5 s = do
  let request = srvRequest s
      response = srvResponse s
  reqText <- msgMD5Text request
  resText <- msgMD5Text response
  return . md5sum $ B.append reqText resText