1use crate::TargetError;
16use rustfs_config::notify::{ARN_PREFIX, DEFAULT_ARN_PARTITION, DEFAULT_ARN_SERVICE};
17use serde::{Deserialize, Deserializer, Serialize, Serializer};
18use std::fmt;
19use std::str::FromStr;
20use thiserror::Error;
21
22#[derive(Debug, Error)]
23pub enum TargetIDError {
24 #[error("Invalid TargetID format '{0}', expect 'ID:Name'")]
25 InvalidFormat(String),
26}
27
28#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
30pub struct TargetID {
31 pub id: String,
32 pub name: String,
33}
34
35impl TargetID {
36 pub fn new(id: String, name: String) -> Self {
37 Self { id, name }
38 }
39
40 pub fn to_arn(&self, region: &str) -> ARN {
42 ARN {
43 target_id: self.clone(),
44 region: region.to_string(),
45 service: DEFAULT_ARN_SERVICE.to_string(), partition: DEFAULT_ARN_PARTITION.to_string(), }
48 }
49}
50
51impl fmt::Display for TargetID {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 write!(f, "{}:{}", self.id, self.name)
54 }
55}
56
57impl FromStr for TargetID {
58 type Err = TargetIDError;
59
60 fn from_str(s: &str) -> Result<Self, Self::Err> {
61 let parts: Vec<&str> = s.splitn(2, ':').collect();
62 if parts.len() == 2 {
63 Ok(TargetID {
64 id: parts[0].to_string(),
65 name: parts[1].to_string(),
66 })
67 } else {
68 Err(TargetIDError::InvalidFormat(s.to_string()))
69 }
70 }
71}
72
73impl Serialize for TargetID {
74 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
75 where
76 S: Serializer,
77 {
78 serializer.serialize_str(&self.to_string())
79 }
80}
81
82impl<'de> Deserialize<'de> for TargetID {
83 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
84 where
85 D: Deserializer<'de>,
86 {
87 let s = String::deserialize(deserializer)?;
88 TargetID::from_str(&s).map_err(serde::de::Error::custom)
89 }
90}
91
92#[derive(Debug, Error)]
93pub enum ArnError {
94 #[error("Invalid ARN format '{0}'")]
95 InvalidFormat(String),
96 #[error("ARN component missing")]
97 MissingComponents,
98}
99
100#[derive(Debug, Clone, Eq, PartialEq)]
102pub struct ARN {
103 pub target_id: TargetID,
104 pub region: String,
105 pub service: String,
107 pub partition: String,
109}
110
111impl ARN {
112 pub fn new(target_id: TargetID, region: String) -> Self {
113 ARN {
114 target_id,
115 region,
116 service: DEFAULT_ARN_SERVICE.to_string(), partition: DEFAULT_ARN_PARTITION.to_string(), }
119 }
120
121 #[allow(clippy::inherent_to_string)]
124 pub fn to_arn_string(&self) -> String {
125 if self.target_id.id.is_empty() && self.target_id.name.is_empty() && self.region.is_empty() {
126 return String::new();
127 }
128 format!("{}:{}:{}", ARN_PREFIX, self.region, self.target_id)
129 }
130
131 pub fn parse(s: &str) -> Result<Self, TargetError> {
135 if !s.starts_with(ARN_PREFIX) {
136 return Err(TargetError::InvalidARN(s.to_string()));
137 }
138
139 let tokens: Vec<&str> = s.split(':').collect();
140 if tokens.len() != 6 {
141 return Err(TargetError::InvalidARN(s.to_string()));
142 }
143
144 if tokens[4].is_empty() || tokens[5].is_empty() {
145 return Err(TargetError::InvalidARN(s.to_string()));
146 }
147
148 Ok(ARN {
149 region: tokens[3].to_string(),
150 target_id: TargetID {
151 id: tokens[4].to_string(),
152 name: tokens[5].to_string(),
153 },
154 service: tokens[2].to_string(), partition: tokens[1].to_string(), })
157 }
158}
159
160impl fmt::Display for ARN {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 if self.target_id.id.is_empty() && self.target_id.name.is_empty() && self.region.is_empty() {
163 return Ok(());
165 }
166 write!(
167 f,
168 "arn:{}:{}:{}:{}:{}",
169 self.partition, self.service, self.region, self.target_id.id, self.target_id.name
170 )
171 }
172}
173
174impl FromStr for ARN {
175 type Err = ArnError;
176
177 fn from_str(s: &str) -> Result<Self, Self::Err> {
178 let parts: Vec<&str> = s.split(':').collect();
179 if parts.len() < 6 {
180 return Err(ArnError::InvalidFormat(s.to_string()));
181 }
182
183 if parts[0] != "arn" {
184 return Err(ArnError::InvalidFormat(s.to_string()));
185 }
186
187 let partition = parts[1].to_string();
188 let service = parts[2].to_string();
189 let region = parts[3].to_string();
190 let id = parts[4].to_string();
191 let name = parts[5..].join(":"); if id.is_empty() || name.is_empty() {
194 return Err(ArnError::MissingComponents);
195 }
196
197 Ok(ARN {
198 target_id: TargetID { id, name },
199 region,
200 service,
201 partition,
202 })
203 }
204}
205
206impl Serialize for ARN {
208 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
209 where
210 S: Serializer,
211 {
212 serializer.serialize_str(&self.to_string())
213 }
214}
215
216impl<'de> Deserialize<'de> for ARN {
217 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
218 where
219 D: Deserializer<'de>,
220 {
221 let s = String::deserialize(deserializer)?;
223 if s.is_empty() {
224 return Ok(ARN {
228 target_id: TargetID {
229 id: String::new(),
230 name: String::new(),
231 },
232 region: String::new(),
233 service: DEFAULT_ARN_SERVICE.to_string(),
234 partition: DEFAULT_ARN_PARTITION.to_string(),
235 });
236 }
237 ARN::from_str(&s).map_err(serde::de::Error::custom)
238 }
239}