    -- Copyright 2020 United States Government as represented by the Administrator
    -- of the National Aeronautics and Space Administration. All Rights Reserved.
    --
    -- Disclaimers
    --
    -- Licensed under the Apache License, Version 2.0 (the "License"); you may
    -- not use this file except in compliance with the License. You may obtain a
    -- copy of the License at
    --
    --      https://www.apache.org/licenses/LICENSE-2.0
    --
    -- Unless required by applicable law or agreed to in writing, software
    -- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    -- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    -- License for the specific language governing permissions and limitations
    -- under the License.
    --
    -- | Auxiliary functions for working with values of type 'ByteString'.
    module Data.ByteString.Extra where

    import           Control.Exception    ( catch )
    import qualified Data.ByteString.Lazy as B
    import           System.IO.Error      ( isDoesNotExistError )

    -- * Safe I/O

    -- | Safely read a file into a lazy 'ByteString', returning a 'Left' error
    -- message if the file cannot be opened.
    safeReadFile :: FilePath -> IO (Either String B.ByteString)
    safeReadFile :: String -> IO (Either String ByteString)
    safeReadFile String
    fp =
      IO (Either String ByteString)
    -> (IOError -> IO (Either String ByteString))
    -> IO (Either String ByteString)
    forall e a. Exception e => IO a -> (e -> IO a) -> IO a
    catch (ByteString -> Either String ByteString
    forall a. a -> Either String a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (ByteString -> Either String ByteString)
    -> IO ByteString -> IO (Either String ByteString)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> String -> IO ByteString
    B.readFile String
    fp) ((IOError -> IO (Either String ByteString))
     -> IO (Either String ByteString))
    -> (IOError -> IO (Either String ByteString))
    -> IO (Either String ByteString)
    forall a b. (a -> b) -> a -> b
    $ \IOError
    e ->
        if IOError -> Bool
    isDoesNotExistError IOError
    e
          then Either String ByteString -> IO (Either String ByteString)
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either String ByteString -> IO (Either String ByteString))
    -> Either String ByteString -> IO (Either String ByteString)
    forall a b. (a -> b) -> a -> b
    $ String -> Either String ByteString
    forall a b. a -> Either a b
    Left (String -> Either String ByteString)
    -> String -> Either String ByteString
    forall a b. (a -> b) -> a -> b
    $ String -> String
    strByteStringFileNotFound String
    fp
          else Either String ByteString -> IO (Either String ByteString)
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either String ByteString -> IO (Either String ByteString))
    -> Either String ByteString -> IO (Either String ByteString)
    forall a b. (a -> b) -> a -> b
    $ String -> Either String ByteString
    forall a b. a -> Either a b
    Left (String -> Either String ByteString)
    -> String -> Either String ByteString
    forall a b. (a -> b) -> a -> b
    $ String -> String
    strByteStringCannotOpenFile String
    fp

    -- ** Error messages

    -- | File-not-found message.
    strByteStringFileNotFound :: FilePath -> String
    strByteStringFileNotFound :: String -> String
    strByteStringFileNotFound String
    fp = String
    "File not found: " String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    fp

    -- | Cannot-open-file message.
    strByteStringCannotOpenFile :: FilePath -> String
    strByteStringCannotOpenFile :: String -> String
    strByteStringCannotOpenFile String
    fp = String
    "Error opening file " String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    fp