1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
// Copyright 2019 Palantir Technologies, Inc.
//
// 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
//
// http://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.
//! Runtime support for Conjure error types.
//!
//! Conjure errors are represented by a struct implementing the `ErrorType` trait. The struct's fields are the error's
//! parameters, and the trait implementation stores the remainder of the error's information.
#![warn(clippy::all, missing_docs)]
extern crate self as conjure_error;
use conjure_object::Uuid;
use serde::{Serialize, Serializer};
use crate::ser::{ParametersSerializer, StringSeed};
pub use crate::error::*;
pub use crate::types::*;
use serde::de::DeserializeSeed;
mod error;
mod ser;
#[allow(clippy::all, missing_docs)]
#[rustfmt::skip]
mod types;
impl ErrorCode {
/// Returns the HTTP status code associated with the error code.
#[inline]
pub fn status_code(&self) -> u16 {
match self {
ErrorCode::PermissionDenied => 403,
ErrorCode::InvalidArgument => 400,
ErrorCode::NotFound => 404,
ErrorCode::Conflict => 409,
ErrorCode::RequestEntityTooLarge => 413,
ErrorCode::FailedPrecondition => 500,
ErrorCode::Internal => 500,
ErrorCode::Timeout => 500,
ErrorCode::CustomClient => 400,
ErrorCode::CustomServer => 500,
}
}
}
/// A trait implemented by Conjure error types.
pub trait ErrorType {
/// Returns the error's code.
fn code(&self) -> ErrorCode;
/// Returns the error's name.
///
/// The name must be formatted like `NamespaceName:ErrorName`.
fn name(&self) -> &str;
/// Returns the error's instance ID, if it stores one.
///
/// Conjure-generated error types return `None`, but other implementations like those for `SerializableError`
/// and `WithInstanceId` return a value.
fn instance_id(&self) -> Option<Uuid>;
/// Returns a sorted slice of the names of the error's safe parameters.
fn safe_args(&self) -> &'static [&'static str];
/// Wraps the error in another that overrides its instance ID.
#[inline]
fn with_instance_id(self, instance_id: Uuid) -> WithInstanceId<Self>
where
Self: Sized,
{
WithInstanceId {
error: self,
instance_id,
}
}
}
impl<T> ErrorType for &T
where
T: ?Sized + ErrorType,
{
#[inline]
fn code(&self) -> ErrorCode {
(**self).code()
}
#[inline]
fn name(&self) -> &str {
(**self).name()
}
#[inline]
fn instance_id(&self) -> Option<Uuid> {
(**self).instance_id()
}
#[inline]
fn safe_args(&self) -> &'static [&'static str] {
(**self).safe_args()
}
}
/// An `ErrorType` which wraps another and overrides its instance ID.
pub struct WithInstanceId<T> {
error: T,
instance_id: Uuid,
}
impl<T> ErrorType for WithInstanceId<T>
where
T: ErrorType,
{
fn code(&self) -> ErrorCode {
self.error.code()
}
fn name(&self) -> &str {
self.error.name()
}
fn instance_id(&self) -> Option<Uuid> {
Some(self.instance_id)
}
fn safe_args(&self) -> &'static [&'static str] {
self.error.safe_args()
}
}
impl<T> Serialize for WithInstanceId<T>
where
T: Serialize,
{
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.error.serialize(s)
}
}
/// Encodes a Conjure error into its serialized form.
///
/// The error's instance ID will be randomly generated if not provided by the error.
///
/// # Panics
///
/// Panics if the error type does not serialize as a struct.
pub fn encode<T>(error: &T) -> SerializableError
where
T: ErrorType + Serialize,
{
let mut builder = SerializableError::builder();
let parameters = error
.serialize(ParametersSerializer)
.expect("failed to serialize error parameters");
for (key, value) in parameters {
if let Ok(value) = StringSeed.deserialize(value) {
builder.insert_parameters(key, value);
}
}
builder
.error_code(error.code())
.error_name(error.name())
.error_instance_id(error.instance_id().unwrap_or_else(Uuid::new_v4))
.build()
}