tonic_richer_error/std_messages/
help.rs

1use prost::{DecodeError, Message};
2use prost_types::Any;
3
4use super::super::pb;
5use super::super::{FromAny, IntoAny};
6
7/// Used at the `links` field of the [`Help`] struct. Describes a URL link.
8#[derive(Clone, Debug)]
9pub struct HelpLink {
10    /// Description of what the link offers.
11    pub description: String,
12
13    /// URL of the link.
14    pub url: String,
15}
16
17impl HelpLink {
18    /// Creates a new [`HelpLink`] struct.
19    pub fn new(description: impl Into<String>, url: impl Into<String>) -> Self {
20        HelpLink {
21            description: description.into(),
22            url: url.into(),
23        }
24    }
25}
26
27/// Used to encode/decode the `Help` standard error message described in
28/// [error_details.proto]. Provides links to documentation or for performing
29/// an out-of-band action.
30///
31/// [error_details.proto]: https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto
32#[derive(Clone, Debug)]
33pub struct Help {
34    /// Links pointing to additional information on how to handle the error.
35    pub links: Vec<HelpLink>,
36}
37
38impl Help {
39    /// Type URL of the `Help` standard error message type.
40    pub const TYPE_URL: &'static str = "type.googleapis.com/google.rpc.Help";
41
42    /// Creates a new [`Help`] struct.
43    pub fn new(links: Vec<HelpLink>) -> Self {
44        Help { links }
45    }
46
47    /// Creates a new [`Help`] struct with a single [`HelpLink`] in `links`.
48    pub fn with_link(description: impl Into<String>, url: impl Into<String>) -> Self {
49        Help {
50            links: vec![HelpLink {
51                description: description.into(),
52                url: url.into(),
53            }],
54        }
55    }
56}
57
58impl Help {
59    /// Adds a [`HelpLink`] to [`Help`]'s `links` vector.
60    pub fn add_link(
61        &mut self,
62        description: impl Into<String>,
63        url: impl Into<String>,
64    ) -> &mut Self {
65        self.links.append(&mut vec![HelpLink {
66            description: description.into(),
67            url: url.into(),
68        }]);
69        self
70    }
71
72    /// Returns `true` if [`Help`]'s `links` vector is empty, and `false` if it
73    /// is not.
74    pub fn is_empty(&self) -> bool {
75        self.links.is_empty()
76    }
77}
78
79impl IntoAny for Help {
80    fn into_any(self) -> Any {
81        let detail_data = pb::Help {
82            links: self
83                .links
84                .into_iter()
85                .map(|v| pb::help::Link {
86                    description: v.description,
87                    url: v.url,
88                })
89                .collect(),
90        };
91
92        Any {
93            type_url: Help::TYPE_URL.to_string(),
94            value: detail_data.encode_to_vec(),
95        }
96    }
97}
98
99impl FromAny for Help {
100    fn from_any(any: Any) -> Result<Self, DecodeError> {
101        let buf: &[u8] = &any.value;
102        let help = pb::Help::decode(buf)?;
103
104        let quota_failure = Help {
105            links: help
106                .links
107                .into_iter()
108                .map(|v| HelpLink {
109                    description: v.description,
110                    url: v.url,
111                })
112                .collect(),
113        };
114
115        Ok(quota_failure)
116    }
117}
118
119#[cfg(test)]
120mod tests {
121
122    use super::super::super::{FromAny, IntoAny};
123    use super::Help;
124
125    #[test]
126    fn gen_quota_failure() {
127        let mut help = Help::new(Vec::new());
128        let formatted = format!("{:?}", help);
129
130        println!("empty Help -> {formatted}");
131
132        let expected = "Help { links: [] }";
133
134        assert!(
135            formatted.eq(expected),
136            "empty Help differs from expected result"
137        );
138
139        assert!(
140            help.is_empty(),
141            "empty Help returns 'false' from .is_empty()"
142        );
143
144        help.add_link("link to resource a", "resource-a.example.local")
145            .add_link("link to resource b", "resource-b.example.local");
146
147        let formatted = format!("{:?}", help);
148
149        println!("filled Help -> {formatted}");
150
151        let expected_filled = "Help { links: [HelpLink { description: \"link to resource a\", url: \"resource-a.example.local\" }, HelpLink { description: \"link to resource b\", url: \"resource-b.example.local\" }] }";
152
153        assert!(
154            formatted.eq(expected_filled),
155            "filled Help differs from expected result"
156        );
157
158        assert!(
159            help.is_empty() == false,
160            "filled Help returns 'true' from .is_empty()"
161        );
162
163        let gen_any = help.into_any();
164
165        let formatted = format!("{:?}", gen_any);
166
167        println!("Any generated from Help -> {formatted}");
168
169        let expected = "Any { type_url: \"type.googleapis.com/google.rpc.Help\", value: [10, 46, 10, 18, 108, 105, 110, 107, 32, 116, 111, 32, 114, 101, 115, 111, 117, 114, 99, 101, 32, 97, 18, 24, 114, 101, 115, 111, 117, 114, 99, 101, 45, 97, 46, 101, 120, 97, 109, 112, 108, 101, 46, 108, 111, 99, 97, 108, 10, 46, 10, 18, 108, 105, 110, 107, 32, 116, 111, 32, 114, 101, 115, 111, 117, 114, 99, 101, 32, 98, 18, 24, 114, 101, 115, 111, 117, 114, 99, 101, 45, 98, 46, 101, 120, 97, 109, 112, 108, 101, 46, 108, 111, 99, 97, 108] }";
170
171        assert!(
172            formatted.eq(expected),
173            "Any from filled Help differs from expected result"
174        );
175
176        let br_details = match Help::from_any(gen_any) {
177            Err(error) => panic!("Error generating Help from Any: {:?}", error),
178            Ok(from_any) => from_any,
179        };
180
181        let formatted = format!("{:?}", br_details);
182
183        println!("Help generated from Any -> {formatted}");
184
185        assert!(
186            formatted.eq(expected_filled),
187            "Help from Any differs from expected result"
188        );
189    }
190}