conjure_error/
lib.rs

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