///|
/// Failures raised by the typed frontend client.
pub(all) suberror ClientFailure {
  BridgeUnavailable
  RequestCancelled
  InvalidContract(message~ : String)
  RemoteFailure(code~ : String, message~ : String, detail~ : String?)
  TransportFailure(message~ : String)
  ResponseDecode(message~ : String)
  EventDecode(message~ : String)
} derive(Debug, Eq)

///|
/// Returns a user-facing description of this client failure.
pub fn ClientFailure::message(self : ClientFailure) -> String {
  match self {
    BridgeUnavailable => "the Proton bridge is not available on this page"
    RequestCancelled => "the Proton bridge request was cancelled"
    InvalidContract(message~) => "invalid Proton contract: " + message
    RemoteFailure(code~, message~, detail~) =>
      match detail {
        Some(detail) =>
          "remote command failed [" +
          code +
          "]: " +
          message +
          " (" +
          detail +
          ")"
        None => "remote command failed [" + code + "]: " + message
      }
    TransportFailure(message~) => "Proton bridge transport failed: " + message
    ResponseDecode(message~) =>
      "failed to decode the Proton command response: " + message
    EventDecode(message~) =>
      "failed to decode the Proton event payload: " + message
  }
}

///|
impl Show for ClientFailure with fn output(self, logger) {
  logger.write_string(self.message())
}

///|
priv struct BridgeFailureWire {
  code : String
  message : String
  detail : String?
} derive(FromJson)

///|
fn decode_bridge_failure(raw : String) -> ClientFailure {
  let wire : BridgeFailureWire = @json.from_json(@json.parse(raw)) catch {
    _ => return TransportFailure(message=raw)
  }
  match wire.code {
    "bridge_unavailable" => BridgeUnavailable
    "request_cancelled" => RequestCancelled
    "transport_failure" => TransportFailure(message=wire.message)
    code => RemoteFailure(code~, message=wire.message, detail=wire.detail)
  }
}