1use std::cmp::Ordering;
9use std::fmt;
10
11#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
13#[non_exhaustive]
14pub struct Origin {
15 inner: Inner,
16}
17
18impl fmt::Display for Origin {
19 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20 use Inner::*;
21
22 match self.inner {
23 Imds => write!(f, "IMDS"),
24 ProfileFile(Kind::Shared) => write!(f, "shared profile file"),
25 ProfileFile(Kind::Service) => write!(f, "service profile file"),
26 EnvironmentVariable(Kind::Shared) => write!(f, "shared environment variable"),
27 EnvironmentVariable(Kind::Service) => write!(f, "service environment variable"),
28 Programmatic(Kind::Shared) => write!(f, "shared client"),
29 Programmatic(Kind::Service) => write!(f, "service client"),
30 Unknown => write!(f, "unknown"),
31 }
32 }
33}
34
35impl Origin {
36 pub fn unknown() -> Self {
38 Self {
39 inner: Inner::Unknown,
40 }
41 }
42
43 pub fn imds() -> Self {
45 Self { inner: Inner::Imds }
46 }
47
48 pub fn shared_config() -> Self {
50 Self {
51 inner: Inner::Programmatic(Kind::Shared),
52 }
53 }
54
55 pub fn service_config() -> Self {
57 Self {
58 inner: Inner::Programmatic(Kind::Service),
59 }
60 }
61
62 pub fn shared_environment_variable() -> Self {
64 Self {
65 inner: Inner::EnvironmentVariable(Kind::Shared),
66 }
67 }
68
69 pub fn service_environment_variable() -> Self {
71 Self {
72 inner: Inner::EnvironmentVariable(Kind::Service),
73 }
74 }
75
76 pub fn shared_profile_file() -> Self {
78 Self {
79 inner: Inner::ProfileFile(Kind::Shared),
80 }
81 }
82
83 pub fn service_profile_file() -> Self {
85 Self {
86 inner: Inner::ProfileFile(Kind::Service),
87 }
88 }
89
90 pub fn is_client_config(&self) -> bool {
92 matches!(
93 self,
94 Origin {
95 inner: Inner::Programmatic(..),
96 ..
97 }
98 )
99 }
100}
101
102impl Default for Origin {
103 fn default() -> Self {
104 Self::unknown()
105 }
106}
107
108#[derive(Debug, Clone, Copy)]
109enum Inner {
110 Imds,
111 ProfileFile(Kind),
112 EnvironmentVariable(Kind),
113 Programmatic(Kind),
114 Unknown,
115}
116
117impl Inner {
118 pub(self) fn is_unknown(&self) -> bool {
119 matches!(self, Inner::Unknown)
120 }
121}
122
123impl PartialEq for Inner {
125 fn eq(&self, other: &Self) -> bool {
126 use Inner::*;
127
128 match (self, other) {
129 (Imds, Imds) => true,
130 (Programmatic(a), Programmatic(b)) => a == b,
131 (EnvironmentVariable(a), EnvironmentVariable(b)) => a == b,
132 (ProfileFile(a), ProfileFile(b)) => a == b,
133 _ => false,
134 }
135 }
136}
137
138impl PartialOrd for Inner {
139 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
140 use Inner::*;
141
142 if self.is_unknown() || other.is_unknown() {
143 return None;
144 }
145
146 match self {
147 Imds => Some(Ordering::Less),
149 ProfileFile(kind) => match other {
151 Imds => Some(Ordering::Greater),
152 ProfileFile(other_kind) => kind.partial_cmp(other_kind),
153 _ => Some(Ordering::Less),
154 },
155 EnvironmentVariable(kind) => match other {
157 Imds | ProfileFile(_) => Some(Ordering::Greater),
158 EnvironmentVariable(other_kind) => kind.partial_cmp(other_kind),
159 _ => Some(Ordering::Less),
160 },
161 Programmatic(kind) => match other {
163 Imds | EnvironmentVariable(_) | ProfileFile(_) => Some(Ordering::Greater),
164 Programmatic(other_kind) => kind.partial_cmp(other_kind),
165 _ => unreachable!(
166 "When we have something higher than programmatic we can update this case."
167 ),
168 },
169 _ => unreachable!(),
170 }
171 }
172}
173
174#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
175enum Kind {
176 Shared,
177 Service,
178}
179
180#[cfg(test)]
181mod test {
182 use super::Origin;
183
184 #[test]
185 fn test_precedence_low_to_high() {
186 let list = [
188 Origin::imds(),
189 Origin::shared_profile_file(),
190 Origin::service_profile_file(),
191 Origin::shared_environment_variable(),
192 Origin::service_environment_variable(),
193 Origin::shared_config(),
194 Origin::service_config(),
195 ];
196
197 for window in list.windows(2) {
198 let &[a, b] = window else { unreachable!() };
199 assert!(a < b);
200 }
201 }
202
203 #[test]
204 fn test_precedence_high_to_low() {
205 let list = [
207 Origin::service_config(),
208 Origin::shared_config(),
209 Origin::service_environment_variable(),
210 Origin::shared_environment_variable(),
211 Origin::service_profile_file(),
212 Origin::shared_profile_file(),
213 Origin::imds(),
214 ];
215
216 for window in list.windows(2) {
217 let &[a, b] = window else { unreachable!() };
218 assert!(a > b);
219 }
220 }
221
222 #[test]
223 fn test_unknown_is_not_equal() {
224 assert_ne!(Origin::unknown(), Origin::imds());
225 assert_ne!(Origin::unknown(), Origin::shared_config());
226 assert_ne!(Origin::unknown(), Origin::service_config());
227 assert_ne!(Origin::unknown(), Origin::shared_environment_variable());
228 assert_ne!(Origin::unknown(), Origin::service_environment_variable());
229 assert_ne!(Origin::unknown(), Origin::shared_profile_file());
230 assert_ne!(Origin::unknown(), Origin::service_profile_file());
231 assert_ne!(Origin::unknown(), Origin::unknown());
232 }
233
234 #[test]
235 fn test_self_equality() {
236 assert_eq!(Origin::imds(), Origin::imds());
237 assert_eq!(Origin::shared_config(), Origin::shared_config());
238 assert_eq!(Origin::service_config(), Origin::service_config());
239 assert_eq!(
240 Origin::shared_environment_variable(),
241 Origin::shared_environment_variable()
242 );
243 assert_eq!(
244 Origin::service_environment_variable(),
245 Origin::service_environment_variable()
246 );
247 assert_eq!(Origin::shared_profile_file(), Origin::shared_profile_file());
248 assert_eq!(
249 Origin::service_profile_file(),
250 Origin::service_profile_file()
251 );
252 }
253}