packages feed

HsSVN-0.4.2: Subversion/Error.hsc

{- -*- haskell -*- -}

{-# OPTIONS_HADDOCK prune #-}

-- |Common exception handling for Subversion. The C API of the
-- Subversion returns an error as a function result, but in HsSVN
-- errors are thrown as an 'Control.Exception.Exception'.

#include "HsSVN.h"

module Subversion.Error
    ( SvnError
    , SVN_ERROR_T -- private

    , wrapSvnError -- private

    , svnErrCode
    , svnErrMsg

    , svnErr -- private

    , SvnErrCode(..)
    )
    where

import           Control.Exception
import           Data.Dynamic
import           Foreign
import           Foreign.C.String
import           Foreign.C.Types
import           Subversion.Types

-- |@'SvnError'@ represents a Subversion error.
newtype SvnError
    = SvnError (ForeignPtr SVN_ERROR_T)
      deriving (Typeable)

data SVN_ERROR_T

instance Show SvnError where
    show e = "SvnError " ++ show (svnErrCode e) ++ " " ++ show (svnErrMsg e)

instance Exception SvnError


foreign import ccall "svn_err_best_message"
        _best_message :: Ptr SVN_ERROR_T -> Ptr CChar -> APR_SIZE_T -> IO (Ptr CChar)

foreign import ccall "&svn_error_clear"
        _clear :: FunPtr (Ptr SVN_ERROR_T -> IO ())


maxErrMsgLen :: Int
maxErrMsgLen = 255


withSvnErrorPtr :: SvnError -> (Ptr SVN_ERROR_T -> IO a) -> IO a
withSvnErrorPtr (SvnError err) = withForeignPtr err

-- |@'svnErrCode' err@ returns a 'SvnErrCode' for an error object.
svnErrCode :: SvnError -> SvnErrCode
svnErrCode err
    = unsafePerformIO $
      withSvnErrorPtr err $ \ errPtr -> 
      do num <- (#peek svn_error_t, apr_err) errPtr
         return $ statusToErrCode num

-- |@'svnErrMsg' err@ returns an error message for an error object.
svnErrMsg :: SvnError -> String
svnErrMsg err
    = unsafePerformIO $
      withSvnErrorPtr err $ \ errPtr ->
      allocaArray maxErrMsgLen $ \ bufPtr ->
          _best_message errPtr bufPtr (fromIntegral maxErrMsgLen)
               >>= peekCString


wrapSvnError :: Ptr SVN_ERROR_T -> IO (Maybe SvnError)
wrapSvnError errPtr
    | errPtr == nullPtr
        = return Nothing
    | otherwise
        = fmap (Just . SvnError) (newForeignPtr _clear errPtr)


svnErr :: IO (Ptr SVN_ERROR_T) -> IO ()
svnErr f
    = do err <- wrapSvnError =<< f
         case err of
           Nothing -> return ()
           Just e  -> throwIO e

-- |@'SvnErrCode'@ represents a Subversion error code. As you see, not
-- all errors are translated to Haskell constructors yet. Uncovered
-- error codes are temporarily represented as @'UnknownError' num@.
data SvnErrCode
    = AprEEXIST         -- ^ APR EEXIST error: typically it means
                        --   something you tried to create was already
                        --   there.
    | AprENOENT         -- ^ APR ENOENT error: typically it means
                        --   something you tried to use wasn't there.
    | DirNotEmpty       -- ^ The directory needs to be empty but it's not.
    | ReposLocked       -- ^ The repository was locked, perhaps for db
                        --   recovery.
    | FsAlreadyExists   -- ^ The item already existed in filesystem.
    | FsConflict        -- ^ Merge conflict has occured during commit.
    | FsNoSuchRevision  -- ^ It was an invalid filesystem revision
                        --   number.
    | FsNotDirectory    -- ^ It was not a filesystem directory entry.
    | FsNotFile         -- ^ It was not a filesystem file entry.
    | FsNotFound        -- ^ It wasn't there in filesystem.
    | UnknownError !Int -- ^ Any other errors than above. You
                        --   shouldn't rely on the absence of
                        --   appropriate 'SvnErrCode' constructors
                        --   because they may be added in the future
                        --   version of HsSVN. If that happens to you,
                        --   your code stops working.
      deriving (Show, Eq, Typeable)

statusToErrCode :: APR_STATUS_T -> SvnErrCode
statusToErrCode (#const APR_EEXIST                 ) = AprEEXIST
statusToErrCode (#const APR_ENOENT                 ) = AprENOENT
statusToErrCode (#const SVN_ERR_DIR_NOT_EMPTY      ) = DirNotEmpty
statusToErrCode (#const SVN_ERR_REPOS_LOCKED       ) = ReposLocked
statusToErrCode (#const SVN_ERR_FS_ALREADY_EXISTS  ) = FsAlreadyExists
statusToErrCode (#const SVN_ERR_FS_CONFLICT        ) = FsConflict
statusToErrCode (#const SVN_ERR_FS_NO_SUCH_REVISION) = FsNoSuchRevision
statusToErrCode (#const SVN_ERR_FS_NOT_DIRECTORY   ) = FsNotDirectory
statusToErrCode (#const SVN_ERR_FS_NOT_FILE        ) = FsNotFile
statusToErrCode (#const SVN_ERR_FS_NOT_FOUND       ) = FsNotFound
statusToErrCode n                                    = UnknownError (fromIntegral n)