///|
/// Wraps the bot's identify queue with Discord's daily session start limit.
/// After the queue grants a slot, the live `session_start_limit` is read from
/// `GET /gateway/bot`; a shard whose IDENTIFY would exceed it is held while
/// `Bot::run` ends with `SessionStartLimitExceeded`. Exceeding that limit
/// resets the bot token, so an exhausted limit fails closed. A limit that
/// cannot be read is reported through the app's warning hook and the shard
/// identifies anyway, because a REST outage must not stop a running bot.
priv struct IdentifyGuard {
  bot : BotCore
  client : @dhttp.Client
  inner : &@queue.IdentifyQueue
  refusals : @aqueue.Queue[ManagedShardEvent]
}

///|
impl @queue.IdentifyQueue for IdentifyGuard with fn wait_for_identify(
  self,
  shard_id,
) {
  self.inner.wait_for_identify(shard_id)
  let info = self.bot.gateway_bot_info(self.client) catch {
    error => {
      self.bot.app_.warn(
        @app.Executor(
          source="gateway",
          message="identifying shard \{shard_id} without reading the session start limit: \{Repr(error)}",
        ),
      )
      return
    }
  }
  if session_start_refusal(info.session_start_limit, required=1) is Some(error) {
    self.refusals.put(IdentifyRefused(error))
    // Hold the shard here; `Bot::run` raises the error and cancels the task
    // group, which tears this shard down without sending IDENTIFY.
    let parked : @async.Semaphore = Semaphore(1, initial_value=0)
    parked.acquire()
  }
}