1use std::borrow::Cow;
2use std::fmt;
3use std::ops::Deref;
4
5use kube::api::GroupVersionKind;
6use serde::{
7 Deserialize,
8 Deserializer,
9 Serialize,
10 Serializer,
11 de,
12};
13
14use crate::errors::*;
15use crate::prelude::*;
16
17#[derive(Clone, Debug, Hash, Eq, PartialEq)]
24pub struct GVK(GroupVersionKind);
25
26impl GVK {
27 pub fn new(group: &str, version: &str, kind: &str) -> GVK {
28 GVK(GroupVersionKind::gvk(group, version, kind))
29 }
30
31 pub fn from_dynamic_obj(obj: &DynamicObject) -> anyhow::Result<GVK> {
32 match &obj.types {
33 Some(t) => Ok(GVK(t.try_into()?)),
34 None => bail!("no type data present"),
35 }
36 }
37
38 pub fn from_owner_ref(rf: &metav1::OwnerReference) -> anyhow::Result<GVK> {
39 let parts: Vec<_> = rf.api_version.split('/').collect();
40
41 if parts.len() == 1 {
42 Ok(GVK(GroupVersionKind::gvk("", parts[0], &rf.kind)))
43 } else if parts.len() == 2 {
44 Ok(GVK(GroupVersionKind::gvk(parts[0], parts[1], &rf.kind)))
45 } else {
46 bail!("invalid format for api_version: {}", rf.api_version);
47 }
48 }
49
50 pub fn into_type_meta(&self) -> TypeMeta {
51 TypeMeta {
52 api_version: self.0.api_version(),
53 kind: self.0.kind.clone(),
54 }
55 }
56}
57
58impl Deref for GVK {
60 type Target = GroupVersionKind;
61
62 fn deref(&self) -> &Self::Target {
63 &self.0
64 }
65}
66
67impl fmt::Display for GVK {
68 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69 let mut group = Cow::from(&self.0.group);
70 if !group.is_empty() {
71 group.to_mut().push('/');
72 }
73
74 write!(f, "{group}{}.{}", self.0.version, self.0.kind)
75 }
76}
77
78impl Serialize for GVK {
79 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
80 where
81 S: Serializer,
82 {
83 serializer.serialize_str(&format!("{self}"))
85 }
86}
87
88struct GVKVisitor;
89
90impl de::Visitor<'_> for GVKVisitor {
91 type Value = GVK;
92
93 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
94 formatter.write_str("a GroupVersionKind in the format group/version.kind")
95 }
96
97 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
98 where
99 E: de::Error,
100 {
101 let p1: Vec<_> = value.split('/').collect();
102 let (group, rest) = match p1.len() {
103 2 => (p1[0], p1[1]),
104 1 => ("", p1[0]),
105 _ => return Err(E::custom(format!("invalid format for gvk: {value}"))),
106 };
107 let p2: Vec<_> = rest.split('.').collect();
108 let (version, kind) = match p2.len() {
109 2 => (p2[0], p2[1]),
110 _ => return Err(E::custom(format!("invalid format for gvk: {value}"))),
111 };
112
113 Ok(GVK(GroupVersionKind::gvk(group, version, kind)))
114 }
115}
116
117impl<'de> Deserialize<'de> for GVK {
118 fn deserialize<D>(deserializer: D) -> Result<GVK, D::Error>
119 where
120 D: Deserializer<'de>,
121 {
122 deserializer.deserialize_str(GVKVisitor)
123 }
124}
125
126#[cfg(test)]
127mod test {
128 use assertables::*;
129 use serde::de::IntoDeserializer;
130 use serde::de::value::{
131 Error as SerdeError,
132 StrDeserializer,
133 };
134 use sk_testutils::*;
135
136 use super::*;
137
138 #[rstest]
139 fn test_serialize() {
140 assert_eq!(serde_json::to_string(&GVK::new("foo", "v1", "bar")).unwrap(), "\"foo/v1.bar\"");
143 assert_eq!(serde_json::to_string(&GVK::new("", "v1", "bar")).unwrap(), "\"v1.bar\"");
144 }
145
146 #[rstest]
147 fn test_deserialize() {
148 let d1: StrDeserializer<SerdeError> = "foo/v1.bar".into_deserializer();
149 assert_eq!(GVK::deserialize(d1).unwrap(), GVK::new("foo", "v1", "bar"));
150
151 let d2: StrDeserializer<SerdeError> = "/v1.bar".into_deserializer();
152 assert_eq!(GVK::deserialize(d2).unwrap(), GVK::new("", "v1", "bar"));
153
154 let d3: StrDeserializer<SerdeError> = "v1.bar".into_deserializer();
155 assert_eq!(GVK::deserialize(d3).unwrap(), GVK::new("", "v1", "bar"));
156
157 let d4: StrDeserializer<SerdeError> = "asdf".into_deserializer();
158 assert_err!(GVK::deserialize(d4));
159
160 let d5: StrDeserializer<SerdeError> = "foo/asdf/asdf".into_deserializer();
161 assert_err!(GVK::deserialize(d5));
162 }
163}