1use std::fmt;
2
3use crate::error::ErrorKind;
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash)]
9pub struct IdTokenAudience(String);
10
11impl IdTokenAudience {
12 pub fn new(audience: impl Into<String>) -> Self {
13 Self(audience.into())
14 }
15
16 pub fn as_str(&self) -> &str {
17 &self.0
18 }
19}
20
21impl fmt::Display for IdTokenAudience {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 f.write_str(&self.0)
24 }
25}
26
27#[derive(Debug, Clone, PartialEq, Eq, Hash)]
30pub struct ServiceAccountEmail(String);
31
32impl ServiceAccountEmail {
33 pub fn new(email: impl Into<String>) -> Self {
34 Self(email.into())
35 }
36
37 pub(crate) fn from_impersonation_url(url: &str) -> crate::error::Result<Self> {
40 let resource = url.rsplit('/').next().unwrap_or_default();
41 let email = resource.split(':').next().unwrap_or_default();
42 if email.is_empty() {
43 Err(ErrorKind::InvalidImpersonationUrl(url.to_string()).into())
44 } else {
45 Ok(Self::new(email))
46 }
47 }
48}
49
50impl fmt::Display for ServiceAccountEmail {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 f.write_str(&self.0)
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 #[test]
61 fn impersonation_url_names_the_service_account_before_the_method() {
62 let url = "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/invoker@orders.iam.gserviceaccount.com:generateAccessToken";
63
64 assert_eq!(
65 ServiceAccountEmail::from_impersonation_url(url).unwrap(),
66 ServiceAccountEmail::new("invoker@orders.iam.gserviceaccount.com")
67 );
68 }
69
70 #[test]
71 fn impersonation_url_without_a_service_account_is_invalid() {
72 let url =
73 "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/:generateAccessToken";
74
75 assert!(matches!(
76 ServiceAccountEmail::from_impersonation_url(url)
77 .unwrap_err()
78 .into_kind(),
79 ErrorKind::InvalidImpersonationUrl(_)
80 ));
81 }
82}