-- |
-- Module      : Amazonka.Waiter
-- Copyright   : (c) 2013-2023 Brendan Hay
-- License     : Mozilla Public License, v. 2.0.
-- Maintainer  : Brendan Hay <brendan.g.hay+amazonka@gmail.com>
-- Stability   : provisional
-- Portability : non-portable (GHC extensions)
module Amazonka.Waiter
  ( -- * Types
    Acceptor,
    Accept (..),
    Wait (..),

    -- ** Lenses
    wait_name,
    wait_attempts,
    wait_delay,
    wait_acceptors,

    -- * Acceptors
    accept,

    -- * Matchers
    matchAll,
    matchAny,
    matchNonEmpty,
    matchError,
    matchStatus,

    -- * Util
    nonEmptyText,
  )
where

import Amazonka.Core.Lens.Internal
  ( Fold,
    Lens,
    allOf,
    anyOf,
    to,
    (^..),
    (^?),
  )
import Amazonka.Data
import Amazonka.Error (_HttpStatus)
import Amazonka.Prelude
import Amazonka.Types
import qualified Data.Text as Text
import qualified Network.HTTP.Client as Client

type Acceptor a = Request a -> Either Error (ClientResponse (AWSResponse a)) -> Maybe Accept

data Accept
  = AcceptSuccess
  | AcceptFailure
  | AcceptRetry
  deriving stock (Accept -> Accept -> Bool
(Accept -> Accept -> Bool)
-> (Accept -> Accept -> Bool) -> Eq Accept
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Accept -> Accept -> Bool
== :: Accept -> Accept -> Bool
$c/= :: Accept -> Accept -> Bool
/= :: Accept -> Accept -> Bool
Eq, Int -> Accept -> ShowS
[Accept] -> ShowS
Accept -> String
(Int -> Accept -> ShowS)
-> (Accept -> String) -> ([Accept] -> ShowS) -> Show Accept
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Accept -> ShowS
showsPrec :: Int -> Accept -> ShowS
$cshow :: Accept -> String
show :: Accept -> String
$cshowList :: [Accept] -> ShowS
showList :: [Accept] -> ShowS
Show)

instance ToLog Accept where
  build :: Accept -> ByteStringBuilder
build = \case
    Accept
AcceptSuccess -> ByteStringBuilder
"Success"
    Accept
AcceptFailure -> ByteStringBuilder
"Failure"
    Accept
AcceptRetry -> ByteStringBuilder
"Retry"

-- | Timing and acceptance criteria to check fulfillment of a remote operation.
data Wait a = Wait
  { forall a. Wait a -> ByteString
name :: ByteString,
    forall a. Wait a -> Int
attempts :: Int,
    forall a. Wait a -> Seconds
delay :: Seconds,
    forall a. Wait a -> [Acceptor a]
acceptors :: [Acceptor a]
  }

{-# INLINE wait_name #-}
wait_name :: Lens' (Wait a) ByteString
wait_name :: forall a (f :: * -> *).
Functor f =>
(ByteString -> f ByteString) -> Wait a -> f (Wait a)
wait_name ByteString -> f ByteString
f w :: Wait a
w@Wait {ByteString
$sel:name:Wait :: forall a. Wait a -> ByteString
name :: ByteString
name} = ByteString -> f ByteString
f ByteString
name f ByteString -> (ByteString -> Wait a) -> f (Wait a)
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \ByteString
name' -> Wait a
w {$sel:name:Wait :: ByteString
name = ByteString
name'}

{-# INLINE wait_attempts #-}
wait_attempts :: forall a. Lens' (Wait a) Int
wait_attempts :: forall a (f :: * -> *).
Functor f =>
(Int -> f Int) -> Wait a -> f (Wait a)
wait_attempts Int -> f Int
f w :: Wait a
w@Wait {Int
$sel:attempts:Wait :: forall a. Wait a -> Int
attempts :: Int
attempts} = Int -> f Int
f Int
attempts f Int -> (Int -> Wait a) -> f (Wait a)
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \Int
attempts' -> (Wait a
w :: Wait a) {$sel:attempts:Wait :: Int
attempts = Int
attempts'}

{-# INLINE wait_delay #-}
wait_delay :: Lens' (Wait a) Seconds
wait_delay :: forall a (f :: * -> *).
Functor f =>
(Seconds -> f Seconds) -> Wait a -> f (Wait a)
wait_delay Seconds -> f Seconds
f w :: Wait a
w@Wait {Seconds
$sel:delay:Wait :: forall a. Wait a -> Seconds
delay :: Seconds
delay} = Seconds -> f Seconds
f Seconds
delay f Seconds -> (Seconds -> Wait a) -> f (Wait a)
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \Seconds
delay' -> Wait a
w {$sel:delay:Wait :: Seconds
delay = Seconds
delay'}

{-# INLINE wait_acceptors #-}
wait_acceptors :: Lens (Wait a) (Wait b) [Acceptor a] [Acceptor b]
wait_acceptors :: forall a b (f :: * -> *).
Functor f =>
([Acceptor a] -> f [Acceptor b]) -> Wait a -> f (Wait b)
wait_acceptors [Acceptor a] -> f [Acceptor b]
f w :: Wait a
w@Wait {[Acceptor a]
$sel:acceptors:Wait :: forall a. Wait a -> [Acceptor a]
acceptors :: [Acceptor a]
acceptors} = [Acceptor a] -> f [Acceptor b]
f [Acceptor a]
acceptors f [Acceptor b] -> ([Acceptor b] -> Wait b) -> f (Wait b)
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \[Acceptor b]
acceptors' -> Wait a
w {$sel:acceptors:Wait :: [Acceptor b]
acceptors = [Acceptor b]
acceptors'}

accept :: Wait a -> Acceptor a
accept :: forall a. Wait a -> Acceptor a
accept Wait a
w Request a
rq Either Error (ClientResponse (AWSResponse a))
rs = [Accept] -> Maybe Accept
forall a. [a] -> Maybe a
listToMaybe ([Accept] -> Maybe Accept)
-> ([Request a
     -> Either Error (ClientResponse (AWSResponse a)) -> Maybe Accept]
    -> [Accept])
-> [Request a
    -> Either Error (ClientResponse (AWSResponse a)) -> Maybe Accept]
-> Maybe Accept
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Request a
  -> Either Error (ClientResponse (AWSResponse a)) -> Maybe Accept)
 -> Maybe Accept)
-> [Request a
    -> Either Error (ClientResponse (AWSResponse a)) -> Maybe Accept]
-> [Accept]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (\Request a
-> Either Error (ClientResponse (AWSResponse a)) -> Maybe Accept
f -> Request a
-> Either Error (ClientResponse (AWSResponse a)) -> Maybe Accept
f Request a
rq Either Error (ClientResponse (AWSResponse a))
rs) ([Request a
  -> Either Error (ClientResponse (AWSResponse a)) -> Maybe Accept]
 -> Maybe Accept)
-> [Request a
    -> Either Error (ClientResponse (AWSResponse a)) -> Maybe Accept]
-> Maybe Accept
forall a b. (a -> b) -> a -> b
$ Wait a
-> [Request a
    -> Either Error (ClientResponse (AWSResponse a)) -> Maybe Accept]
forall a. Wait a -> [Acceptor a]
acceptors Wait a
w

matchAll :: Eq b => b -> Accept -> Fold (AWSResponse a) b -> Acceptor a
matchAll :: forall b a.
Eq b =>
b -> Accept -> Fold (AWSResponse a) b -> Acceptor a
matchAll b
x Accept
a Fold (AWSResponse a) b
l = (AWSResponse a -> Bool)
-> Accept
-> Request a
-> Either Error (Response (AWSResponse a))
-> Maybe Accept
forall a. (AWSResponse a -> Bool) -> Accept -> Acceptor a
match (Getting All (AWSResponse a) b
-> (b -> Bool) -> AWSResponse a -> Bool
forall s a. Getting All s a -> (a -> Bool) -> s -> Bool
allOf Getting All (AWSResponse a) b
Fold (AWSResponse a) b
l (b -> b -> Bool
forall a. Eq a => a -> a -> Bool
== b
x)) Accept
a

matchAny :: Eq b => b -> Accept -> Fold (AWSResponse a) b -> Acceptor a
matchAny :: forall b a.
Eq b =>
b -> Accept -> Fold (AWSResponse a) b -> Acceptor a
matchAny b
x Accept
a Fold (AWSResponse a) b
l = (AWSResponse a -> Bool)
-> Accept
-> Request a
-> Either Error (Response (AWSResponse a))
-> Maybe Accept
forall a. (AWSResponse a -> Bool) -> Accept -> Acceptor a
match (Getting Any (AWSResponse a) b
-> (b -> Bool) -> AWSResponse a -> Bool
forall s a. Getting Any s a -> (a -> Bool) -> s -> Bool
anyOf Getting Any (AWSResponse a) b
Fold (AWSResponse a) b
l (b -> b -> Bool
forall a. Eq a => a -> a -> Bool
== b
x)) Accept
a

matchNonEmpty :: Bool -> Accept -> Fold (AWSResponse a) b -> Acceptor a
matchNonEmpty :: forall a b. Bool -> Accept -> Fold (AWSResponse a) b -> Acceptor a
matchNonEmpty Bool
x Accept
a Fold (AWSResponse a) b
l = (AWSResponse a -> Bool)
-> Accept
-> Request a
-> Either Error (Response (AWSResponse a))
-> Maybe Accept
forall a. (AWSResponse a -> Bool) -> Accept -> Acceptor a
match (\AWSResponse a
rs -> [b] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (AWSResponse a
rs AWSResponse a -> Getting (Endo [b]) (AWSResponse a) b -> [b]
forall s a. s -> Getting (Endo [a]) s a -> [a]
^.. Getting (Endo [b]) (AWSResponse a) b
Fold (AWSResponse a) b
l) Bool -> Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Bool
x) Accept
a

matchStatus :: Int -> Accept -> Acceptor a
matchStatus :: forall a. Int -> Accept -> Acceptor a
matchStatus Int
x Accept
a Request a
_ = \case
  Right ClientResponse (AWSResponse a)
rs | Int
x Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Status -> Int
forall a. Enum a => a -> Int
fromEnum (ClientResponse (AWSResponse a) -> Status
forall body. Response body -> Status
Client.responseStatus ClientResponse (AWSResponse a)
rs) -> Accept -> Maybe Accept
forall a. a -> Maybe a
Just Accept
a
  Left Error
e | Int -> Maybe Int
forall a. a -> Maybe a
Just Int
x Maybe Int -> Maybe Int -> Bool
forall a. Eq a => a -> a -> Bool
== (Status -> Int
forall a. Enum a => a -> Int
fromEnum (Status -> Int) -> Maybe Status -> Maybe Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Error
e Error -> Getting (First Status) Error Status -> Maybe Status
forall s a. s -> Getting (First a) s a -> Maybe a
^? Getting (First Status) Error Status
forall a. AsError a => Traversal' a Status
Traversal' Error Status
_HttpStatus) -> Accept -> Maybe Accept
forall a. a -> Maybe a
Just Accept
a
  Either Error (ClientResponse (AWSResponse a))
_ -> Maybe Accept
forall a. Maybe a
Nothing

matchError :: ErrorCode -> Accept -> Acceptor a
matchError :: forall a. ErrorCode -> Accept -> Acceptor a
matchError ErrorCode
c Accept
a Request a
_ = \case
  Left Error
e | ErrorCode -> Maybe ErrorCode
forall a. a -> Maybe a
Just ErrorCode
c Maybe ErrorCode -> Maybe ErrorCode -> Bool
forall a. Eq a => a -> a -> Bool
== Error
e Error
-> Getting (First ErrorCode) Error ErrorCode -> Maybe ErrorCode
forall s a. s -> Getting (First a) s a -> Maybe a
^? (ServiceError -> Const (First ErrorCode) ServiceError)
-> Error -> Const (First ErrorCode) Error
forall a. AsError a => Prism' a ServiceError
Prism' Error ServiceError
_ServiceError ((ServiceError -> Const (First ErrorCode) ServiceError)
 -> Error -> Const (First ErrorCode) Error)
-> ((ErrorCode -> Const (First ErrorCode) ErrorCode)
    -> ServiceError -> Const (First ErrorCode) ServiceError)
-> Getting (First ErrorCode) Error ErrorCode
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ServiceError -> ErrorCode)
-> (ErrorCode -> Const (First ErrorCode) ErrorCode)
-> ServiceError
-> Const (First ErrorCode) ServiceError
forall (p :: * -> * -> *) (f :: * -> *) s a.
(Profunctor p, Contravariant f) =>
(s -> a) -> Optic' p f s a
to ServiceError -> ErrorCode
code -> Accept -> Maybe Accept
forall a. a -> Maybe a
Just Accept
a
  Either Error (ClientResponse (AWSResponse a))
_ -> Maybe Accept
forall a. Maybe a
Nothing

match :: (AWSResponse a -> Bool) -> Accept -> Acceptor a
match :: forall a. (AWSResponse a -> Bool) -> Accept -> Acceptor a
match AWSResponse a -> Bool
f Accept
a Request a
_ = \case
  Right ClientResponse (AWSResponse a)
rs | AWSResponse a -> Bool
f (ClientResponse (AWSResponse a) -> AWSResponse a
forall body. Response body -> body
Client.responseBody ClientResponse (AWSResponse a)
rs) -> Accept -> Maybe Accept
forall a. a -> Maybe a
Just Accept
a
  Either Error (ClientResponse (AWSResponse a))
_ -> Maybe Accept
forall a. Maybe a
Nothing

nonEmptyText :: Fold a Text -> Fold a Bool
nonEmptyText :: forall a. Fold a Text -> Fold a Bool
nonEmptyText Fold a Text
f = (Text -> f Text) -> a -> f a
Fold a Text
f ((Text -> f Text) -> a -> f a)
-> ((Bool -> f Bool) -> Text -> f Text)
-> (Bool -> f Bool)
-> a
-> f a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> Bool) -> (Bool -> f Bool) -> Text -> f Text
forall (p :: * -> * -> *) (f :: * -> *) s a.
(Profunctor p, Contravariant f) =>
(s -> a) -> Optic' p f s a
to Text -> Bool
Text.null