Skip to content

Commit 3f1c7c6

Browse files
authored
feat: Introduce common resonse type helpers (#1109)
Before we drop NumString, IntString, BoolString from the cli copy them into the SDK so that those can be also reused in the tui.
1 parent 656b53d commit 3f1c7c6

File tree

2 files changed

+301
-21
lines changed

2 files changed

+301
-21
lines changed

openstack_sdk/src/types.rs

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@
1616
1717
#![allow(dead_code)]
1818
use secrecy::{ExposeSecret, SecretString};
19-
use serde::{Deserialize, Serialize};
2019
use std::fmt;
2120

2221
pub mod api_version;
22+
pub mod common;
2323
pub mod compute;
2424
pub mod identity;
2525

@@ -29,6 +29,7 @@ use std::pin::Pin;
2929
use std::task::{Context, Poll};
3030

3131
pub use crate::types::api_version::ApiVersion;
32+
pub use common::{BoolString, IdAndName, IntString, NameOrId, NumString};
3233

3334
/// Supported Service Types
3435
#[derive(Debug, Clone, Eq, PartialEq)]
@@ -124,26 +125,6 @@ impl AsyncRead for BoxedAsyncRead {
124125
}
125126
}
126127

127-
/// A reference to a resource by its Name and ID.
128-
#[derive(Deserialize, Debug, Clone, Serialize, Eq, PartialEq)]
129-
pub struct IdAndName {
130-
/// The name of the entity.
131-
pub name: String,
132-
/// The UID for the entity.
133-
pub id: String,
134-
}
135-
136-
/// A reference to a resource by either its Name or ID.
137-
#[derive(Clone, Debug, Serialize, PartialEq, Eq, Hash)]
138-
pub enum NameOrId {
139-
/// Resource ID.
140-
#[serde(rename = "id")]
141-
Id(String),
142-
/// Resource name.
143-
#[serde(rename = "name")]
144-
Name(String),
145-
}
146-
147128
/// Status of the resource
148129
#[derive(Debug, Default, PartialEq)]
149130
pub enum EntryStatus {

openstack_sdk/src/types/common.rs

Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
// Licensed under the Apache License, Version 2.0 (the "License");
2+
// you may not use this file except in compliance with the License.
3+
// You may obtain a copy of the License at
4+
//
5+
// http://www.apache.org/licenses/LICENSE-2.0
6+
//
7+
// Unless required by applicable law or agreed to in writing, software
8+
// distributed under the License is distributed on an "AS IS" BASIS,
9+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
// See the License for the specific language governing permissions and
11+
// limitations under the License.
12+
//
13+
// SPDX-License-Identifier: Apache-2.0
14+
15+
//! Common types that can be used in responses of the API operations
16+
use serde::{de::Visitor, Deserialize, Deserializer, Serialize};
17+
use std::fmt;
18+
19+
/// IntString
20+
///
21+
/// Integer type holder that can be deserialized from Integer or String
22+
#[derive(Clone, Debug, PartialEq, Serialize)]
23+
#[serde(transparent)]
24+
pub struct IntString(u64);
25+
26+
impl fmt::Display for IntString {
27+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
28+
write!(f, "{}", self.0)
29+
}
30+
}
31+
32+
impl<'de> Deserialize<'de> for IntString {
33+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
34+
where
35+
D: Deserializer<'de>,
36+
{
37+
struct MyVisitor;
38+
39+
impl Visitor<'_> for MyVisitor {
40+
type Value = IntString;
41+
42+
fn expecting(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
43+
fmt.write_str("integer or string")
44+
}
45+
46+
fn visit_u64<E>(self, val: u64) -> Result<Self::Value, E>
47+
where
48+
E: serde::de::Error,
49+
{
50+
Ok(IntString(val))
51+
}
52+
53+
fn visit_str<E>(self, val: &str) -> Result<Self::Value, E>
54+
where
55+
E: serde::de::Error,
56+
{
57+
match val.parse::<u64>() {
58+
Ok(val) => self.visit_u64(val),
59+
Err(_) => Ok(IntString(0)),
60+
}
61+
}
62+
}
63+
64+
deserializer.deserialize_any(MyVisitor)
65+
}
66+
}
67+
68+
/// NumString
69+
///
70+
/// Number type holder that can be deserialized from Number or String
71+
#[derive(Clone, Debug, PartialEq, Serialize)]
72+
#[serde(transparent)]
73+
pub struct NumString(f64);
74+
75+
impl fmt::Display for NumString {
76+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77+
write!(f, "{}", self.0)
78+
}
79+
}
80+
impl<'de> Deserialize<'de> for NumString {
81+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
82+
where
83+
D: Deserializer<'de>,
84+
{
85+
struct MyVisitor;
86+
87+
impl Visitor<'_> for MyVisitor {
88+
type Value = NumString;
89+
90+
fn expecting(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
91+
fmt.write_str("number or string")
92+
}
93+
94+
fn visit_u64<E>(self, val: u64) -> Result<Self::Value, E>
95+
where
96+
E: serde::de::Error,
97+
{
98+
Ok(NumString(val as f64))
99+
}
100+
101+
fn visit_f64<E>(self, val: f64) -> Result<Self::Value, E>
102+
where
103+
E: serde::de::Error,
104+
{
105+
Ok(NumString(val))
106+
}
107+
108+
fn visit_str<E>(self, val: &str) -> Result<Self::Value, E>
109+
where
110+
E: serde::de::Error,
111+
{
112+
match val.parse::<f64>() {
113+
Ok(val) => self.visit_f64(val),
114+
Err(_) => Ok(NumString(0.0)),
115+
}
116+
}
117+
}
118+
119+
deserializer.deserialize_any(MyVisitor)
120+
}
121+
}
122+
123+
/// BoolString
124+
///
125+
/// Boolean type holder that can be deserialized from Boolean or String
126+
#[derive(Clone, Debug, PartialEq, Serialize)]
127+
#[serde(transparent)]
128+
pub struct BoolString(bool);
129+
130+
impl fmt::Display for BoolString {
131+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
132+
write!(f, "{}", self.0)
133+
}
134+
}
135+
136+
impl<'de> Deserialize<'de> for BoolString {
137+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
138+
where
139+
D: Deserializer<'de>,
140+
{
141+
struct MyVisitor;
142+
143+
impl Visitor<'_> for MyVisitor {
144+
type Value = BoolString;
145+
146+
fn expecting(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
147+
fmt.write_str("boolean or string")
148+
}
149+
150+
fn visit_bool<E>(self, val: bool) -> Result<Self::Value, E>
151+
where
152+
E: serde::de::Error,
153+
{
154+
Ok(BoolString(val))
155+
}
156+
157+
fn visit_str<E>(self, val: &str) -> Result<Self::Value, E>
158+
where
159+
E: serde::de::Error,
160+
{
161+
match val.parse::<bool>() {
162+
Ok(val) => self.visit_bool(val),
163+
Err(_) => Ok(BoolString(false)),
164+
}
165+
}
166+
}
167+
168+
deserializer.deserialize_any(MyVisitor)
169+
}
170+
}
171+
172+
/// A reference to a resource by its Name and ID.
173+
#[derive(Deserialize, Debug, Clone, Serialize, Eq, PartialEq)]
174+
pub struct IdAndName {
175+
/// The name of the entity.
176+
pub name: String,
177+
/// The UID for the entity.
178+
pub id: String,
179+
}
180+
181+
/// A reference to a resource by either its Name or ID.
182+
#[derive(Clone, Debug, Serialize, PartialEq, Eq, Hash)]
183+
pub enum NameOrId {
184+
/// Resource ID.
185+
#[serde(rename = "id")]
186+
Id(String),
187+
/// Resource name.
188+
#[serde(rename = "name")]
189+
Name(String),
190+
}
191+
192+
#[cfg(test)]
193+
mod tests {
194+
use serde::de::value::{
195+
BoolDeserializer, Error as ValueError, F64Deserializer, StrDeserializer, U64Deserializer,
196+
};
197+
use serde::de::IntoDeserializer;
198+
199+
use super::*;
200+
201+
#[test]
202+
fn test_intstring_int() {
203+
let deserializer: U64Deserializer<ValueError> = 1u64.into_deserializer();
204+
assert_eq!(IntString::deserialize(deserializer), Ok(IntString(1)));
205+
}
206+
207+
#[test]
208+
fn test_intstring_empty_str() {
209+
let deserializer: StrDeserializer<ValueError> = "".into_deserializer();
210+
assert_eq!(IntString::deserialize(deserializer), Ok(IntString(0)));
211+
}
212+
213+
#[test]
214+
fn test_intstring_str() {
215+
let deserializer: StrDeserializer<ValueError> = "5".into_deserializer();
216+
assert_eq!(IntString::deserialize(deserializer), Ok(IntString(5)));
217+
}
218+
219+
#[test]
220+
fn test_intstring_str_error() {
221+
let deserializer: StrDeserializer<ValueError> = "foo5".into_deserializer();
222+
assert_eq!(IntString::deserialize(deserializer), Ok(IntString(0)));
223+
}
224+
225+
#[test]
226+
fn test_intstring_display() {
227+
assert_eq!(IntString(4).to_string(), "4");
228+
}
229+
230+
#[test]
231+
fn test_numstring_u64() {
232+
let deserializer: U64Deserializer<ValueError> = 1u64.into_deserializer();
233+
assert_eq!(NumString::deserialize(deserializer), Ok(NumString(1.0)));
234+
}
235+
236+
#[test]
237+
fn test_numstring_f64() {
238+
let deserializer: F64Deserializer<ValueError> = 2.3_f64.into_deserializer();
239+
assert_eq!(NumString::deserialize(deserializer), Ok(NumString(2.3)));
240+
}
241+
242+
#[test]
243+
fn test_numstring_empty_str() {
244+
let deserializer: StrDeserializer<ValueError> = "".into_deserializer();
245+
assert_eq!(NumString::deserialize(deserializer), Ok(NumString(0.0)));
246+
}
247+
248+
#[test]
249+
fn test_numstring_str() {
250+
let deserializer: StrDeserializer<ValueError> = "5".into_deserializer();
251+
assert_eq!(NumString::deserialize(deserializer), Ok(NumString(5.0)));
252+
}
253+
254+
#[test]
255+
fn test_numstring_str_error() {
256+
let deserializer: StrDeserializer<ValueError> = "5f".into_deserializer();
257+
assert_eq!(NumString::deserialize(deserializer), Ok(NumString(0.0)));
258+
}
259+
260+
#[test]
261+
fn test_numstring_display() {
262+
assert_eq!(NumString(4.1).to_string(), "4.1");
263+
}
264+
265+
#[test]
266+
fn test_boolstring_str() {
267+
let deserializer: StrDeserializer<ValueError> = "true".into_deserializer();
268+
assert_eq!(BoolString::deserialize(deserializer), Ok(BoolString(true)));
269+
}
270+
271+
#[test]
272+
fn test_boolstring_str2() {
273+
let deserializer: StrDeserializer<ValueError> = "false".into_deserializer();
274+
assert_eq!(BoolString::deserialize(deserializer), Ok(BoolString(false)));
275+
}
276+
277+
#[test]
278+
fn test_boolstring_str_error() {
279+
let deserializer: StrDeserializer<ValueError> = "foo".into_deserializer();
280+
assert_eq!(BoolString::deserialize(deserializer), Ok(BoolString(false)));
281+
}
282+
283+
#[test]
284+
fn test_boolstring_bool1() {
285+
let deserializer: BoolDeserializer<ValueError> = true.into_deserializer();
286+
assert_eq!(BoolString::deserialize(deserializer), Ok(BoolString(true)));
287+
}
288+
289+
#[test]
290+
fn test_boolstring_bool2() {
291+
let deserializer: BoolDeserializer<ValueError> = false.into_deserializer();
292+
assert_eq!(BoolString::deserialize(deserializer), Ok(BoolString(false)));
293+
}
294+
295+
#[test]
296+
fn test_boolstring_display() {
297+
assert_eq!(BoolString(true).to_string(), "true");
298+
}
299+
}

0 commit comments

Comments
 (0)