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
use super::std_messages::{BadRequest, FieldViolation};

pub(crate) mod vec;

/// Groups the standard error messages structs. Provides associated
/// functions and methods to setup and edit each error message independently.
/// Used when extracting error details from `tonic::Status`, and when
/// creating a `tonic::Status` with error details.
#[non_exhaustive]
#[derive(Clone, Debug)]
pub struct ErrorDetails {
    /// This field stores [`BadRequest`] data, if any.
    pub(crate) bad_request: Option<BadRequest>,
}

impl ErrorDetails {
    /// Generates an [`ErrorDetails`] struct with all fields set to `None`.
    ///
    /// # Examples
    ///
    /// ```
    /// use tonic_types::{ErrorDetails};
    ///
    /// let err_details = ErrorDetails::new();
    /// ```
    pub fn new() -> Self {
        ErrorDetails { bad_request: None }
    }

    /// Generates an [`ErrorDetails`] struct with [`BadRequest`] details and
    /// remaining fields set to `None`.
    ///
    /// # Examples
    ///
    /// ```
    /// use tonic_types::{ErrorDetails, FieldViolation};
    ///
    /// let err_details = ErrorDetails::with_bad_request(vec![
    ///     FieldViolation::new("field_1", "description 1"),
    ///     FieldViolation::new("field_2", "description 2"),
    /// ]);
    /// ```
    pub fn with_bad_request(field_violations: Vec<FieldViolation>) -> Self {
        ErrorDetails {
            bad_request: Some(BadRequest::new(field_violations)),
            ..ErrorDetails::new()
        }
    }

    /// Generates an [`ErrorDetails`] struct with [`BadRequest`] details (one
    /// [`FieldViolation`] set) and remaining fields set to `None`.
    ///
    /// # Examples
    ///
    /// ```
    /// use tonic_types::{ErrorDetails};
    ///
    /// let err_details = ErrorDetails::with_bad_request_violation(
    ///     "field",
    ///     "description",
    /// );
    /// ```
    pub fn with_bad_request_violation(
        field: impl Into<String>,
        description: impl Into<String>,
    ) -> Self {
        ErrorDetails {
            bad_request: Some(BadRequest::with_violation(field, description)),
            ..ErrorDetails::new()
        }
    }

    /// Get [`BadRequest`] details, if any
    pub fn bad_request(&self) -> Option<BadRequest> {
        self.bad_request.clone()
    }

    /// Set [`BadRequest`] details. Can be chained with other `.set_` and
    /// `.add_` [`ErrorDetails`] methods.
    ///
    /// # Examples
    ///
    /// ```
    /// use tonic_types::{ErrorDetails, FieldViolation};
    ///
    /// let mut err_details = ErrorDetails::new();
    ///
    /// err_details.set_bad_request(vec![
    ///     FieldViolation::new("field_1", "description 1"),
    ///     FieldViolation::new("field_2", "description 2"),
    /// ]);
    /// ```
    pub fn set_bad_request(&mut self, violations: Vec<FieldViolation>) -> &mut Self {
        self.bad_request = Some(BadRequest::new(violations));
        self
    }

    /// Adds a [`FieldViolation`] to [`BadRequest`] details. Sets
    /// [`BadRequest`] details if it is not set yet. Can be chained with other
    /// `.set_` and `.add_` [`ErrorDetails`] methods.
    ///
    /// # Examples
    ///
    /// ```
    /// use tonic_types::{ErrorDetails};
    ///
    /// let mut err_details = ErrorDetails::new();
    ///
    /// err_details.add_bad_request_violation("field", "description");
    /// ```
    pub fn add_bad_request_violation(
        &mut self,
        field: impl Into<String>,
        description: impl Into<String>,
    ) -> &mut Self {
        match &mut self.bad_request {
            Some(bad_request) => {
                bad_request.add_violation(field, description);
            }
            None => {
                self.bad_request = Some(BadRequest::with_violation(field, description));
            }
        };
        self
    }

    /// Returns `true` if [`BadRequest`] is set and its `field_violations`
    /// vector is not empty, otherwise returns `false`.
    ///
    /// # Examples
    ///
    /// ```
    /// use tonic_types::{ErrorDetails};
    ///
    /// let mut err_details = ErrorDetails::with_bad_request(vec![]);
    ///
    /// assert_eq!(err_details.has_bad_request_violations(), false);
    ///
    /// err_details.add_bad_request_violation("field", "description");
    ///
    /// assert_eq!(err_details.has_bad_request_violations(), true);
    /// ```
    pub fn has_bad_request_violations(&self) -> bool {
        if let Some(bad_request) = &self.bad_request {
            return !bad_request.field_violations.is_empty();
        }
        false
    }
}