tonic_richer_error/std_messages/
prec_failure.rs1use prost::{DecodeError, Message};
2use prost_types::Any;
3
4use super::super::pb;
5use super::super::{FromAny, IntoAny};
6
7#[derive(Clone, Debug)]
10pub struct PreconditionViolation {
11 pub r#type: String,
17
18 pub subject: String,
20
21 pub description: String,
23}
24
25impl PreconditionViolation {
26 pub fn new(
28 r#type: impl Into<String>,
29 subject: impl Into<String>,
30 description: impl Into<String>,
31 ) -> Self {
32 PreconditionViolation {
33 r#type: r#type.into(),
34 subject: subject.into(),
35 description: description.into(),
36 }
37 }
38}
39
40#[derive(Clone, Debug)]
46pub struct PreconditionFailure {
47 pub violations: Vec<PreconditionViolation>,
49}
50
51impl PreconditionFailure {
52 pub const TYPE_URL: &'static str = "type.googleapis.com/google.rpc.PreconditionFailure";
54
55 pub fn new(violations: Vec<PreconditionViolation>) -> Self {
57 PreconditionFailure {
58 violations: violations,
59 }
60 }
61
62 pub fn with_violation(
65 violation_type: impl Into<String>,
66 subject: impl Into<String>,
67 description: impl Into<String>,
68 ) -> Self {
69 PreconditionFailure {
70 violations: vec![PreconditionViolation {
71 r#type: violation_type.into(),
72 subject: subject.into(),
73 description: description.into(),
74 }],
75 }
76 }
77}
78
79impl PreconditionFailure {
80 pub fn add_violation(
83 &mut self,
84 r#type: impl Into<String>,
85 subject: impl Into<String>,
86 description: impl Into<String>,
87 ) -> &mut Self {
88 self.violations.append(&mut vec![PreconditionViolation {
89 r#type: r#type.into(),
90 subject: subject.into(),
91 description: description.into(),
92 }]);
93 self
94 }
95
96 pub fn is_empty(&self) -> bool {
99 self.violations.is_empty()
100 }
101}
102
103impl IntoAny for PreconditionFailure {
104 fn into_any(self) -> Any {
105 let detail_data = pb::PreconditionFailure {
106 violations: self
107 .violations
108 .into_iter()
109 .map(|v| pb::precondition_failure::Violation {
110 r#type: v.r#type,
111 subject: v.subject,
112 description: v.description,
113 })
114 .collect(),
115 };
116
117 Any {
118 type_url: PreconditionFailure::TYPE_URL.to_string(),
119 value: detail_data.encode_to_vec(),
120 }
121 }
122}
123
124impl FromAny for PreconditionFailure {
125 fn from_any(any: Any) -> Result<Self, DecodeError> {
126 let buf: &[u8] = &any.value;
127 let precondition_failure = pb::PreconditionFailure::decode(buf)?;
128
129 let precondition_failure = PreconditionFailure {
130 violations: precondition_failure
131 .violations
132 .into_iter()
133 .map(|v| PreconditionViolation {
134 r#type: v.r#type,
135 subject: v.subject,
136 description: v.description,
137 })
138 .collect(),
139 };
140
141 Ok(precondition_failure)
142 }
143}
144
145#[cfg(test)]
146mod tests {
147
148 use super::super::super::{FromAny, IntoAny};
149 use super::PreconditionFailure;
150
151 #[test]
152 fn gen_prec_failure() {
153 let mut prec_failure = PreconditionFailure::new(Vec::new());
154 let formatted = format!("{:?}", prec_failure);
155
156 println!("empty PreconditionFailure -> {formatted}");
157
158 let expected = "PreconditionFailure { violations: [] }";
159
160 assert!(
161 formatted.eq(expected),
162 "empty PreconditionFailure differs from expected result"
163 );
164
165 assert!(
166 prec_failure.is_empty(),
167 "empty PreconditionFailure returns 'false' from .is_empty()"
168 );
169
170 prec_failure
171 .add_violation("TOS", "example.local", "Terms of service not accepted")
172 .add_violation("FNF", "example.local", "File not found");
173
174 let formatted = format!("{:?}", prec_failure);
175
176 println!("filled PreconditionFailure -> {formatted}");
177
178 let expected_filled = "PreconditionFailure { violations: [PreconditionViolation { type: \"TOS\", subject: \"example.local\", description: \"Terms of service not accepted\" }, PreconditionViolation { type: \"FNF\", subject: \"example.local\", description: \"File not found\" }] }";
179
180 assert!(
181 formatted.eq(expected_filled),
182 "filled PreconditionFailure differs from expected result"
183 );
184
185 assert!(
186 prec_failure.is_empty() == false,
187 "filled PreconditionFailure returns 'true' from .is_empty()"
188 );
189
190 let gen_any = prec_failure.into_any();
191
192 let formatted = format!("{:?}", gen_any);
193
194 println!("Any generated from PreconditionFailure -> {formatted}");
195
196 let expected = "Any { type_url: \"type.googleapis.com/google.rpc.PreconditionFailure\", value: [10, 51, 10, 3, 84, 79, 83, 18, 13, 101, 120, 97, 109, 112, 108, 101, 46, 108, 111, 99, 97, 108, 26, 29, 84, 101, 114, 109, 115, 32, 111, 102, 32, 115, 101, 114, 118, 105, 99, 101, 32, 110, 111, 116, 32, 97, 99, 99, 101, 112, 116, 101, 100, 10, 36, 10, 3, 70, 78, 70, 18, 13, 101, 120, 97, 109, 112, 108, 101, 46, 108, 111, 99, 97, 108, 26, 14, 70, 105, 108, 101, 32, 110, 111, 116, 32, 102, 111, 117, 110, 100] }";
197
198 assert!(
199 formatted.eq(expected),
200 "Any from filled PreconditionFailure differs from expected result"
201 );
202
203 let br_details = match PreconditionFailure::from_any(gen_any) {
204 Err(error) => panic!("Error generating PreconditionFailure from Any: {:?}", error),
205 Ok(from_any) => from_any,
206 };
207
208 let formatted = format!("{:?}", br_details);
209
210 println!("PreconditionFailure generated from Any -> {formatted}");
211
212 assert!(
213 formatted.eq(expected_filled),
214 "PreconditionFailure from Any differs from expected result"
215 );
216 }
217}