1 use prost::{DecodeError, Message}; 2 use prost_types::Any; 3 4 use crate::richer_error::FromAnyRef; 5 6 use super::super::{pb, FromAny, IntoAny}; 7 8 /// Used at the `field_violations` field of the [`BadRequest`] struct. 9 /// Describes a single bad request field. 10 #[derive(Clone, Debug)] 11 pub struct FieldViolation { 12 /// Path leading to a field in the request body. Value should be a 13 /// sequence of dot-separated identifiers that identify a protocol buffer 14 /// field. 15 pub field: String, 16 17 /// Description of why the field is bad. 18 pub description: String, 19 } 20 21 impl FieldViolation { 22 /// Creates a new [`FieldViolation`] struct. new(field: impl Into<String>, description: impl Into<String>) -> Self23 pub fn new(field: impl Into<String>, description: impl Into<String>) -> Self { 24 FieldViolation { 25 field: field.into(), 26 description: description.into(), 27 } 28 } 29 } 30 31 impl From<pb::bad_request::FieldViolation> for FieldViolation { from(value: pb::bad_request::FieldViolation) -> Self32 fn from(value: pb::bad_request::FieldViolation) -> Self { 33 FieldViolation { 34 field: value.field, 35 description: value.description, 36 } 37 } 38 } 39 40 impl From<FieldViolation> for pb::bad_request::FieldViolation { from(value: FieldViolation) -> Self41 fn from(value: FieldViolation) -> Self { 42 pb::bad_request::FieldViolation { 43 field: value.field, 44 description: value.description, 45 } 46 } 47 } 48 49 /// Used to encode/decode the `BadRequest` standard error message described in 50 /// [error_details.proto]. Describes violations in a client request. Focuses 51 /// on the syntactic aspects of the request. 52 /// 53 /// [error_details.proto]: https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto 54 #[derive(Clone, Debug)] 55 pub struct BadRequest { 56 /// Describes all field violations of the request. 57 pub field_violations: Vec<FieldViolation>, 58 } 59 60 impl BadRequest { 61 /// Type URL of the `BadRequest` standard error message type. 62 pub const TYPE_URL: &'static str = "type.googleapis.com/google.rpc.BadRequest"; 63 64 /// Creates a new [`BadRequest`] struct. new(field_violations: impl Into<Vec<FieldViolation>>) -> Self65 pub fn new(field_violations: impl Into<Vec<FieldViolation>>) -> Self { 66 BadRequest { 67 field_violations: field_violations.into(), 68 } 69 } 70 71 /// Creates a new [`BadRequest`] struct with a single [`FieldViolation`] in 72 /// `field_violations`. with_violation(field: impl Into<String>, description: impl Into<String>) -> Self73 pub fn with_violation(field: impl Into<String>, description: impl Into<String>) -> Self { 74 BadRequest { 75 field_violations: vec![FieldViolation { 76 field: field.into(), 77 description: description.into(), 78 }], 79 } 80 } 81 82 /// Adds a [`FieldViolation`] to [`BadRequest`]'s `field_violations`. add_violation( &mut self, field: impl Into<String>, description: impl Into<String>, ) -> &mut Self83 pub fn add_violation( 84 &mut self, 85 field: impl Into<String>, 86 description: impl Into<String>, 87 ) -> &mut Self { 88 self.field_violations.append(&mut vec![FieldViolation { 89 field: field.into(), 90 description: description.into(), 91 }]); 92 self 93 } 94 95 /// Returns `true` if [`BadRequest`]'s `field_violations` vector is empty, 96 /// and `false` if it is not. is_empty(&self) -> bool97 pub fn is_empty(&self) -> bool { 98 self.field_violations.is_empty() 99 } 100 } 101 102 impl IntoAny for BadRequest { into_any(self) -> Any103 fn into_any(self) -> Any { 104 let detail_data: pb::BadRequest = self.into(); 105 106 Any { 107 type_url: BadRequest::TYPE_URL.to_string(), 108 value: detail_data.encode_to_vec(), 109 } 110 } 111 } 112 113 impl FromAny for BadRequest { 114 #[inline] from_any(any: Any) -> Result<Self, DecodeError>115 fn from_any(any: Any) -> Result<Self, DecodeError> { 116 FromAnyRef::from_any_ref(&any) 117 } 118 } 119 120 impl FromAnyRef for BadRequest { from_any_ref(any: &Any) -> Result<Self, DecodeError>121 fn from_any_ref(any: &Any) -> Result<Self, DecodeError> { 122 let buf: &[u8] = &any.value; 123 let bad_req = pb::BadRequest::decode(buf)?; 124 125 Ok(bad_req.into()) 126 } 127 } 128 129 impl From<pb::BadRequest> for BadRequest { from(value: pb::BadRequest) -> Self130 fn from(value: pb::BadRequest) -> Self { 131 BadRequest { 132 field_violations: value.field_violations.into_iter().map(Into::into).collect(), 133 } 134 } 135 } 136 137 impl From<BadRequest> for pb::BadRequest { from(value: BadRequest) -> Self138 fn from(value: BadRequest) -> Self { 139 pb::BadRequest { 140 field_violations: value.field_violations.into_iter().map(Into::into).collect(), 141 } 142 } 143 } 144 145 #[cfg(test)] 146 mod tests { 147 use super::super::super::{FromAny, IntoAny}; 148 use super::BadRequest; 149 150 #[test] gen_bad_request()151 fn gen_bad_request() { 152 let mut br_details = BadRequest::new(Vec::new()); 153 let formatted = format!("{:?}", br_details); 154 155 let expected = "BadRequest { field_violations: [] }"; 156 157 assert!( 158 formatted.eq(expected), 159 "empty BadRequest differs from expected result" 160 ); 161 162 assert!( 163 br_details.is_empty(), 164 "empty BadRequest returns 'false' from .is_empty()" 165 ); 166 167 br_details 168 .add_violation("field_a", "description_a") 169 .add_violation("field_b", "description_b"); 170 171 let formatted = format!("{:?}", br_details); 172 173 let expected_filled = "BadRequest { field_violations: [FieldViolation { field: \"field_a\", description: \"description_a\" }, FieldViolation { field: \"field_b\", description: \"description_b\" }] }"; 174 175 assert!( 176 formatted.eq(expected_filled), 177 "filled BadRequest differs from expected result" 178 ); 179 180 assert!( 181 !br_details.is_empty(), 182 "filled BadRequest returns 'true' from .is_empty()" 183 ); 184 185 let gen_any = br_details.into_any(); 186 let formatted = format!("{:?}", gen_any); 187 188 let expected = "Any { type_url: \"type.googleapis.com/google.rpc.BadRequest\", value: [10, 24, 10, 7, 102, 105, 101, 108, 100, 95, 97, 18, 13, 100, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 95, 97, 10, 24, 10, 7, 102, 105, 101, 108, 100, 95, 98, 18, 13, 100, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 95, 98] }"; 189 190 assert!( 191 formatted.eq(expected), 192 "Any from filled BadRequest differs from expected result" 193 ); 194 195 let br_details = match BadRequest::from_any(gen_any) { 196 Err(error) => panic!("Error generating BadRequest from Any: {:?}", error), 197 Ok(from_any) => from_any, 198 }; 199 200 let formatted = format!("{:?}", br_details); 201 202 assert!( 203 formatted.eq(expected_filled), 204 "BadRequest from Any differs from expected result" 205 ); 206 } 207 } 208