packages feed

hzk 1.0.1 → 2.0.0

raw patch · 4 files changed

+128/−190 lines, 4 files

Files

hzk.cabal view
@@ -1,7 +1,7 @@ name:          hzk author:        DiegoSouza <dsouza@c0d3.xxx> license:       BSD3-version:       1.0.1+version:       2.0.0 category:      Database homepage:      http://github.com/dgvncsz0f/hzk synopsis:      Haskell client library for Apache Zookeeper
src/Database/Zookeeper.hs view
@@ -1,4 +1,5 @@-{-# LANGUAGE Safe #-}+{-# LANGUAGE Safe          #-}+{-# LANGUAGE TupleSections #-}  -- This file is part of zhk --@@ -174,7 +175,7 @@ setDebugLevel :: ZLogLevel -> IO () setDebugLevel = c_zooSetDebugLevel . fromLogLevel --- | Creates a znode (asynchornous)+-- | Creates a znode create :: Zookeeper        -- ^ Zookeeper handle        -> String@@ -186,17 +187,18 @@        -- ^ The initial ACL of the node. The ACL must not be empty        -> [CreateFlag]        -- ^ Optional, may be empty-       -> (Either ZKError String -> IO ())-       -- ^ The callback function-       -> IO ()-create (Zookeeper zh) path mvalue acls flags callback =+       -> IO (Either ZKError String)+create (Zookeeper zh) path mvalue acls flags =   withCString path $ \pathPtr ->     maybeUseAsCStringLen mvalue $ \(valuePtr, valueLen) ->-      withAclList acls $ \aclPtr -> do-        cStrFn <- wrapStringCompletion callback-        rc     <- c_zooACreate zh pathPtr valuePtr (fromIntegral valueLen) aclPtr (fromCreateFlags flags) cStrFn nullPtr-        unless (isZOK rc) (callback $ Left (toZKError rc))+      withAclList acls $ \aclPtr ->+        allocaBytes pathlen $ \newPathPtr -> do+          rc <- c_zooCreate zh pathPtr valuePtr (fromIntegral valueLen) aclPtr (fromCreateFlags flags) newPathPtr (fromIntegral pathlen)+          if (isZOK rc)+            then fmap Right (peekCString newPathPtr)+            else return (Left $ toZKError rc)     where+      pathlen = length path + 11       maybeUseAsCStringLen Nothing f  = f (nullPtr, -1)       maybeUseAsCStringLen (Just s) f = B.useAsCStringLen s f @@ -232,7 +234,7 @@       cWatcher <- wrapWatcher mwatcher       tryZ (c_zooWExists zh pathPtr cWatcher nullPtr statPtr) (toStat statPtr) --- | Lists the children of a znode (asynchronous)+-- | Lists the children of a znode getChildren :: Zookeeper             -- ^ Zookeeper handle             -> String@@ -241,15 +243,15 @@             -> Maybe Watcher             -- ^ The watch to be set at the server to notify the user             -- if the node changes-            -> (Either ZKError [String] -> IO ())-            -- ^ The callback function-            -> IO ()-getChildren (Zookeeper zh) path mwatcher callback =-  withCString path (\pathPtr -> do-    cWatcher <- wrapWatcher mwatcher-    cStrFn   <- wrapStringsCompletion callback-    rc       <- c_zooAWGetChildren zh pathPtr cWatcher nullPtr cStrFn nullPtr-    unless (isZOK rc) (callback $ Left (toZKError rc)))+            -> IO (Either ZKError [String])+getChildren (Zookeeper zh) path mwatcher =+  withCString path $ \pathPtr ->+    allocaStrVec $ \strVecPtr -> do+      cWatcher <- wrapWatcher mwatcher+      rc       <- c_zooWGetChildren zh pathPtr cWatcher nullPtr strVecPtr+      if (isZOK rc)+        then fmap Right $ toStringList strVecPtr+        else return (Left $ toZKError rc)  -- | Gets the data associated with a znode (asynchronous) get :: Zookeeper@@ -260,15 +262,22 @@     -> Maybe Watcher     -- ^ When provided, a watch will be set at the server to notify     -- the client if the node changes-    -> (Either ZKError (Maybe B.ByteString, Stat) -> IO ())-    -- ^ The callback function-    -> IO ()-get (Zookeeper zh) path mwatcher callback =-  withCString path $ \pathPtr -> do-    cWatcher <- wrapWatcher mwatcher-    cDataFn  <- wrapDataCompletion callback-    rc       <- c_zooAWGet zh pathPtr cWatcher nullPtr cDataFn nullPtr-    unless (isZOK rc) (callback $ Left (toZKError rc))+    -> IO (Either ZKError (Maybe B.ByteString, Stat))+get (Zookeeper zh) path mwatcher =+  withCString path $ \pathPtr ->+    allocaBytes buffSize $ \dataPtr ->+      allocaStat $ \statPtr ->+        alloca $ \buffSizePtr -> do+          poke buffSizePtr (fromIntegral buffSize)+          cWatcher <- wrapWatcher mwatcher+          rc       <- c_zooWGet zh pathPtr cWatcher nullPtr dataPtr buffSizePtr statPtr+          dataSz   <- peek buffSizePtr+          case (isZOK rc, fromIntegral dataSz) of+            (True, -1) -> return . Right =<< fmap (Nothing ,) (toStat statPtr)+            (True, sz) -> return . Right =<< liftM2 (,) (fmap Just $ B.packCStringLen (dataPtr, sz)) (toStat statPtr)+            (False, _) -> return (Left $ toZKError rc)+    where+      buffSize = 1024 * 1024  -- | Sets the data associated with a znode (synchronous) set :: Zookeeper@@ -325,14 +334,15 @@        -> String        -- ^ The name of the znode expressed as a file name with slashes        -- separating ancestors of the znode-       -> (Either ZKError (AclList, Stat) -> IO ())-       -- ^ The callback function-       -> IO ()-getAcl (Zookeeper zh) path callback =-  withCString path $ \pathPtr -> do-    cAclFn <- wrapAclCompletion callback-    rc     <- c_zooAGetAcl zh pathPtr cAclFn nullPtr-    unless (isZOK rc) (callback $ Left (toZKError rc))+       -> IO (Either ZKError (AclList, Stat))+getAcl (Zookeeper zh) path =+  withCString path $ \pathPtr ->+    allocaAclVec $ \aclVecPtr ->+      allocaStat $ \statPtr -> do+        rc <- c_zooGetAcl zh pathPtr aclVecPtr statPtr+        if (isZOK rc)+          then fmap Right $ liftM2 (,) (toAclList aclVecPtr) (toStat statPtr)+          else return (Left $ toZKError rc)  -- | Specify application credentials (asynchronous) --@@ -369,16 +379,7 @@  -- $description ----- This library provides haskell bindings for zookeeper c-library. The--- underlying library exposes two classes of functions: synchronous--- and asynchronous. Whenever possible the synchronous functions are--- used.------ The reason we do not always use the synchronous version is that it--- requires the caller to allocate memory and currently it is--- impossible to know (at least I could not figure it) how much memory--- should be allocated. The asynchronous version has no such problem--- as it manages memory internally.+-- This library provides haskell bindings for zookeeper c-library (mt).  -- $example --@@ -397,8 +398,8 @@ -- >     takeMVar mvar >>= print -- >     where -- >       watcher mvar zh _ ConnectedState _ =--- >         create zh "/foobar" Nothing OpenAclUnsafe [] $ \_ ->--- >           getChildren zh "/" Nothing (putMVar mvar)+-- >         void $ create zh "/foobar" Nothing OpenAclUnsafe []+-- >         getChildren zh "/" Nothing >>= putMVar mvar  -- $notes --   * Watcher callbacks must never block;
src/Database/Zookeeper/CApi.hsc view
@@ -34,20 +34,20 @@        ( -- * C to Haskell          toStat        , toState+       , toAclList        , toZKError        , allocaStat        , toClientId+       , toStringList+       , allocaAclVec+       , allocaStrVec          -- * Haskell to C        , withAclList        , wrapWatcher        , fromLogLevel        , fromCreateFlag        , fromCreateFlags-       , wrapAclCompletion-       , wrapDataCompletion        , wrapVoidCompletion-       , wrapStringCompletion-       , wrapStringsCompletion          -- * Error handling        , tryZ        , isZOK@@ -55,31 +55,30 @@        , whenZOK          -- * C functions        , c_zooSet2-       , c_zooAWGet+       , c_zooWGet        , c_zooState        , c_zooDelete        , c_zooSetAcl-       , c_zooAGetAcl-       , c_zooACreate+       , c_zooCreate+       , c_zooGetAcl        , c_zooAddAuth        , c_zooWExists        , c_zooClientId        , c_zookeeperInit-       , c_zookeeperClose        , c_zooSetWatcher+       , c_zookeeperClose        , c_zooRecvTimeout        , c_isUnrecoverable-       , c_zooAWGetChildren+       , c_zooWGetChildren        , c_zooSetDebugLevel        ) where  #include <zookeeper.h> -import           Foreign.C-import           Foreign.Safe-import qualified Data.ByteString as B-import           Control.Applicative-import           Database.Zookeeper.Types+import Foreign.C+import Foreign.Safe+import Control.Applicative+import Database.Zookeeper.Types  tryZ :: IO CInt -> IO a -> IO (Either ZKError a) tryZ zkIO nextIO = do@@ -183,12 +182,18 @@                    <*> (fmap toPerms ((#peek struct ACL, perms) ptr))         buildList (acl : acc) (n-1) (ptr `plusPtr` (#size struct ACL)) +allocaStrVec :: (Ptr CStrVec -> IO a) -> IO a+allocaStrVec = allocaBytes (#size struct String_vector)++allocaAclVec :: (Ptr CAclVec -> IO a) -> IO a+allocaAclVec = allocaBytes (#size struct ACL_vector)+ withAclList :: AclList -> (Ptr CAclVec -> IO a) -> IO a withAclList CreatorAll cont    = cont c_zooCreatorAclAll withAclList OpenAclUnsafe cont = cont c_zooOpenAclUnsafe withAclList ReadAclUnsafe cont = cont c_zooReadAclUnsafe withAclList (List acls) cont   =-  allocaBytes (#size struct ACL_vector) $ \aclvPtr -> do+  allocaAclVec $ \aclvPtr -> do     (#poke struct ACL_vector, count) aclvPtr count     allocaBytes (count * (#size struct ACL)) $ \aclPtr -> do       (#poke struct ACL_vector, data) aclvPtr aclPtr@@ -258,34 +263,6 @@             else fmap Just (peekCString cpath)   fn (Zookeeper zh) event state path -wrapAclCompletion :: AclCompletion -> IO (FunPtr CAclCompletionFn)-wrapAclCompletion fn =-  c_aclCompletionFn $ \rc aclPtr statPtr _ ->-    fn =<< (onZOK rc $ do-      aclList <- toAclList aclPtr-      stat    <- toStat statPtr-      return (aclList, stat))--wrapDataCompletion :: DataCompletion -> IO (FunPtr CDataCompletionFn)-wrapDataCompletion fn =-  c_dataCompletionFn $ \rc valPtr valLen statPtr _ ->-    fn =<< (onZOK rc $ do-      stat <- toStat statPtr-      if (valLen == -1)-        then return (Nothing, stat)-        else fmap (\s -> (Just s, stat)) (B.packCStringLen (valPtr, fromIntegral valLen)))--wrapStringCompletion :: StringCompletion -> IO (FunPtr CStringCompletionFn)-wrapStringCompletion fn =-  c_stringCompletionFn $ \rc strPtr _ ->-    fn =<< (onZOK rc $ do-      peekCString strPtr)--wrapStringsCompletion :: StringsCompletion -> IO (FunPtr CStringsCompletionFn)-wrapStringsCompletion fn =-  c_stringsCompletionFn $ \rc strvPtr _ ->-    fn =<< (onZOK rc (toStringList strvPtr))- wrapVoidCompletion :: VoidCompletion -> IO (FunPtr CVoidCompletionFn) wrapVoidCompletion fn =   c_voidCompletionFn $ \rc _ -> (fn =<< onZOK rc (return ()))@@ -295,22 +272,6 @@                  -> IO (FunPtr CWatcherFn)  foreign import ccall safe "wrapper"-  c_dataCompletionFn :: CDataCompletionFn-                        -> IO (FunPtr CDataCompletionFn)--foreign import ccall safe "wrapper"-  c_stringsCompletionFn :: CStringsCompletionFn-                        -> IO (FunPtr CStringsCompletionFn)--foreign import ccall safe "wrapper"-  c_stringCompletionFn :: CStringCompletionFn-                       -> IO (FunPtr CStringCompletionFn)--foreign import ccall safe "wrapper"-  c_aclCompletionFn :: CAclCompletionFn-                    -> IO (FunPtr CAclCompletionFn)--foreign import ccall safe "wrapper"   c_voidCompletionFn :: CVoidCompletionFn                      -> IO (FunPtr CVoidCompletionFn) @@ -329,8 +290,8 @@ foreign import ccall safe "zookeeper.h zoo_set_watcher"   c_zooSetWatcher :: Ptr CZHandle -> FunPtr CWatcherFn -> IO () -foreign import ccall safe "zookeeper.h zoo_acreate"-  c_zooACreate :: Ptr CZHandle -> CString -> CString -> CInt -> Ptr CAclVec -> CInt -> FunPtr CStringCompletionFn -> Ptr () -> IO CInt+foreign import ccall safe "zookeeper.h zoo_create"+  c_zooCreate :: Ptr CZHandle -> CString -> CString -> CInt -> Ptr CAclVec -> CInt -> CString -> CInt -> IO CInt  foreign import ccall safe "zookeeper.h zoo_delete"   c_zooDelete :: Ptr CZHandle -> CString -> CInt -> IO CInt@@ -356,20 +317,20 @@ foreign import ccall safe "zookeeper.h zoo_set_debug_level"   c_zooSetDebugLevel :: CInt -> IO () -foreign import ccall safe "zookeeper.h zoo_aget_acl"-  c_zooAGetAcl :: Ptr CZHandle -> CString -> FunPtr CAclCompletionFn -> Ptr () -> IO CInt+foreign import ccall safe "zookeeper.h zoo_get_acl"+  c_zooGetAcl :: Ptr CZHandle -> CString -> Ptr CAclVec -> Ptr CStat -> IO CInt  foreign import ccall safe "zookeeper.h zoo_set_acl"   c_zooSetAcl :: Ptr CZHandle -> CString -> CInt -> Ptr CAclVec -> IO CInt -foreign import ccall safe "zookeeper.h zoo_awget"-  c_zooAWGet :: Ptr CZHandle -> CString -> FunPtr CWatcherFn -> Ptr () -> FunPtr CDataCompletionFn -> Ptr () -> IO CInt+foreign import ccall safe "zookeeper.h zoo_wget"+  c_zooWGet :: Ptr CZHandle -> CString -> FunPtr CWatcherFn -> Ptr () -> CString -> Ptr CInt -> Ptr CStat -> IO CInt  foreign import ccall safe "zookeeper.h zoo_set2"   c_zooSet2 :: Ptr CZHandle -> CString -> CString -> CInt -> CInt -> Ptr CStat -> IO CInt -foreign import ccall safe "zookeeper.h zoo_awget_children"-  c_zooAWGetChildren :: Ptr CZHandle -> CString -> FunPtr CWatcherFn -> Ptr () -> FunPtr CStringsCompletionFn -> Ptr () -> IO CInt+foreign import ccall safe "zookeeper.h zoo_wget_children"+  c_zooWGetChildren :: Ptr CZHandle -> CString -> FunPtr CWatcherFn -> Ptr () -> Ptr CStrVec -> IO CInt  foreign import ccall safe "zookeeper.h &ZOO_CREATOR_ALL_ACL"   c_zooCreatorAclAll :: Ptr CAclVec
src/test-zookeeper.hs view
@@ -62,35 +62,30 @@ testExists zh = testGroup "exists"   [ testCase "exists after create" $ do       let path = chroot "/testExists#1"-      mvar <- newEmptyMVar-      create zh path Nothing OpenAclUnsafe [] (\_ ->-        exists zh path Nothing >>= putMVar mvar . either (const False) (const True))-      takeMVar mvar @? "== Right _"+      create zh path Nothing OpenAclUnsafe []+      exists zh path Nothing >>= (@? "== Right _") . (either (const False) (const True))   , testCase "exists without znode" $ do       let path = chroot "/testExists#2"-      mvar <- newEmptyMVar       exists zh path Nothing >>= (@?= Left NoNodeError)   , testCase "exists(watcher) and create" $ do       let path = chroot "/testExists#3"       mvar <- newEmptyMVar       exists zh path (Just $ watcher mvar) >>= (@?= Left NoNodeError)-      create zh path Nothing OpenAclUnsafe [] (const $ return ())+      create zh path Nothing OpenAclUnsafe []       takeMVar mvar >>= (@?= (CreatedEvent, Just path))   , testCase "exists(watcher) and set" $ do       let path = chroot "/testExists#4"       mvar <- newEmptyMVar-      create zh path Nothing OpenAclUnsafe [] (\_ -> do-        exists zh path (Just $ watcher mvar)-        set zh path Nothing Nothing-        return ())+      create zh path Nothing OpenAclUnsafe []+      exists zh path (Just $ watcher mvar)+      set zh path Nothing Nothing       takeMVar mvar >>= (@?= (ChangedEvent, Just path))   , testCase "exists(watcher) and delete" $ do       let path = chroot "/testExists#5"       mvar <- newEmptyMVar-      create zh path Nothing OpenAclUnsafe [] (\_ -> do-        exists zh path (Just $ watcher mvar)-        delete zh path Nothing-        return ())+      create zh path Nothing OpenAclUnsafe []+      exists zh path (Just $ watcher mvar)+      delete zh path Nothing       takeMVar mvar >>= (@?= (DeletedEvent, Just path))   ]     where@@ -99,32 +94,28 @@ testGet zh = testGroup "get"   [ testCase "get without znode" $ do       let path = chroot "/testGet#1"-      mvar <- newEmptyMVar-      get zh path Nothing (putMVar mvar)-      takeMVar mvar >>= (@?= Left NoNodeError)+      get zh path Nothing >>= (@?= Left NoNodeError)   , testCase "create(nodata) and get" $ do       let path = chroot "/testGet#2"-      mvar <- newEmptyMVar-      create zh path Nothing OpenAclUnsafe [] (\_ ->-        get zh path Nothing (putMVar mvar . either (const False) (isNothing . fst)))-      takeMVar mvar @? "== Right (Nothing, _)"+      create zh path Nothing OpenAclUnsafe []+      get zh path Nothing >>= (@? "== Right (Nothing, _)") . (either (const False) (isNothing . fst))   , testCase "create(data) and get" $ do       let path = chroot "/testGet#3"-      mvar <- newEmptyMVar-      create zh path (Just "foobar") OpenAclUnsafe [] (\_ ->-        get zh path Nothing (putMVar mvar . either (const False) ((== Just "foobar") . fst)))-      takeMVar mvar @? "== Right (Just \"foobar\", _)"+      create zh path (Just "foobar") OpenAclUnsafe []+      get zh path Nothing >>= (@? "== Right (Just \"foobar\", _)") . (either (const False) ((== Just "foobar") . fst))   , testCase "get(watcher) and set" $ do       let path = chroot "/testGet#4"       mvar <- newEmptyMVar-      create zh path Nothing OpenAclUnsafe [] (\_ ->-        get zh path (Just $ watcher mvar) (\_ -> set zh path Nothing Nothing >> return ()))+      create zh path Nothing OpenAclUnsafe []+      get zh path (Just $ watcher mvar)+      set zh path Nothing Nothing       takeMVar mvar >>= (@?= (ChangedEvent, Just path))   , testCase "get(watcher) and delete" $ do       let path = chroot "/testGet#5"       mvar <- newEmptyMVar-      create zh path Nothing OpenAclUnsafe [] (\_ ->-        get zh path (Just $ watcher mvar) (\_ -> delete zh path Nothing >> return ()))+      create zh path Nothing OpenAclUnsafe []+      get zh path (Just $ watcher mvar)+      delete zh path Nothing       takeMVar mvar >>= (@?= (DeletedEvent, Just path))   ]     where@@ -133,48 +124,39 @@ testOwnsEphemeral zh = testGroup "ownsEphemeral"   [ testCase "get ephemeral" $ do     let path = chroot "/testGet#6"-    mvar <- newEmptyMVar-    create zh path Nothing OpenAclUnsafe [Ephemeral] (\_ ->-      get zh path Nothing (\(Right (_, stat)) -> do-        myId <- getClientId zh-        putMVar mvar =<< ownsEphemeral myId stat))-    takeMVar mvar @? "owns ephemeral"+    create zh path Nothing OpenAclUnsafe [Ephemeral]+    (Right (_, stat)) <- get zh path Nothing+    myId <- getClientId zh+    ownsEphemeral myId stat @? "owns ephemeral"   ]  testGetChildren zh = testGroup "getChildren"   [ testCase "getChildren without znode" $ do       let path = chroot "/testGetChildren#1"-      mvar <- newEmptyMVar-      getChildren zh path Nothing (putMVar mvar)-      takeMVar mvar >>= (@?= Left NoNodeError)+      getChildren zh path Nothing >>= (@?= Left NoNodeError)   , testCase "getChildren after create" $ do       let path = chroot "/testGetChildren#2"-      mvar <- newEmptyMVar-      create zh path Nothing OpenAclUnsafe [] (\_ ->-        getChildren zh path Nothing (putMVar mvar))-      takeMVar mvar >>= (@?= Right [])+      create zh path Nothing OpenAclUnsafe []+      getChildren zh path Nothing >>= (@?= Right [])   , testCase "getChildren with one child" $ do       let path = chroot "/testGetChildren#3"-      mvar <- newEmptyMVar-      create zh path Nothing OpenAclUnsafe [] (\_ ->-        create zh (path ++ "/1") Nothing OpenAclUnsafe [] (\_ ->-          getChildren zh path Nothing (putMVar mvar)))-      takeMVar mvar >>= (@?= Right ["1"])+      create zh path Nothing OpenAclUnsafe []+      create zh (path ++ "/1") Nothing OpenAclUnsafe []+      getChildren zh path Nothing >>= (@?= Right ["1"])   , testCase "getChildren(watcher) and create child" $ do       let path = chroot "/testGetChildren#4"       mvar <- newEmptyMVar-      create zh path Nothing OpenAclUnsafe [] (\_ ->-        getChildren zh path (Just $ watcher mvar) (\_ ->-          create zh (path ++ "/1") Nothing OpenAclUnsafe [] (const $ return ())))+      create zh path Nothing OpenAclUnsafe []+      getChildren zh path (Just $ watcher mvar)+      create zh (path ++ "/1") Nothing OpenAclUnsafe []       takeMVar mvar >>= (@?= (ChildEvent, Just path))   , testCase "getChildren(watcher) and delete child" $ do       let path = chroot "/testGetChildren#5"       mvar <- newEmptyMVar-      create zh path Nothing OpenAclUnsafe [] (\_ ->-        create zh (path ++ "/1") Nothing OpenAclUnsafe [] (\_ ->-          getChildren zh path (Just $ watcher mvar) (\_ -> do-            delete zh (path ++ "/1") Nothing-            return ())))+      create zh path Nothing OpenAclUnsafe []+      create zh (path ++ "/1") Nothing OpenAclUnsafe []+      getChildren zh path (Just $ watcher mvar)+      delete zh (path ++ "/1") Nothing       takeMVar mvar >>= (@?= (ChildEvent, Just path))   ]     where@@ -183,10 +165,8 @@ testGetAcl zh = testGroup "getAcl"   [ testCase "getAcl" $ do       let path = chroot "/testGetAcl#1"-      mvar <- newEmptyMVar-      create zh path Nothing OpenAclUnsafe [] (\_ ->-        getAcl zh path (putMVar mvar . either (const 0) (countAcls . fst)))-      takeMVar mvar >>= (@?= 1)+      create zh path Nothing OpenAclUnsafe []+      getAcl zh path >>= (@?= 1) . (either (const 0) (countAcls . fst))   , testCase "getAcl flags" $ do       let path  = chroot "/testGetAcl#2"           flags = [ []@@ -197,12 +177,10 @@                   , [CanRead, CanAdmin, CanWrite, CanCreate, CanDelete]                   ]       forM_ flags $ \flag -> do-        mvar <- newEmptyMVar-        create zh path Nothing (List [Acl "world" "anyone" flag]) [] (\_ -> do-          getAcl zh path (\rc -> do-            delete zh path Nothing-            putMVar mvar (either (const []) (getFlags . fst) rc)))-        takeMVar mvar >>= (@?= flag)+        create zh path Nothing (List [Acl "world" "anyone" flag]) []+        rc <- getAcl zh path+        delete zh path Nothing+        (either (const []) (getFlags . fst) rc) @?= flag   ]     where       countAcls (List xs) = length xs@@ -214,25 +192,23 @@ rmrf :: Zookeeper -> String -> IO () rmrf zh path = do   let childPath name = path ++ "/" ++ name-  mvar <- newEmptyMVar-  getChildren zh path Nothing (putMVar mvar)-  takeMVar mvar >>= either (const $ return ()) (mapM_ (rmrf zh . childPath))-  delete zh path Nothing-  return ()+  children <- getChildren zh path Nothing+  case children of+    Right xs -> mapM_ (rmrf zh . childPath) xs+    Left _   -> return ()+  void $ delete zh path Nothing  main :: IO () main = do   disclaimer   endpoint   <- getEndpoint   waitState  <- newEmptyMVar-  waitCreate <- newEmptyMVar   withZookeeper endpoint 5000 (Just $ watcher waitState) Nothing $ \zh -> do     state <- takeMVar waitState     case state of       ConnectedState -> do         rmrf zh (chroot "")-        create zh (chroot "") Nothing OpenAclUnsafe [] (putMVar waitCreate)-        takeMVar waitCreate+        create zh (chroot "") Nothing OpenAclUnsafe []         defaultMain $ testGroup "Zookeeper" [ testGet zh                                             , testExists zh                                             , testGetAcl zh