Skip to main content

coven_format/
member_access.rs

1//! The storage account carried by member additions and access updates (format.md D6).
2
3use crate::wire::{Decoder, Encoder, Wire};
4use crate::Error;
5use serde::{Deserialize, Serialize};
6
7/// The longest account or key id member access carries (D6).
8pub(crate) const MAX_TEXT: usize = 1024;
9
10/// An account to share with, or the id of a member's S3 key.
11#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
12pub enum MemberAccess {
13    /// The member's provider account email.
14    ProviderAccount(String),
15    /// A member's S3 key, made in the provider's console.
16    S3AccessKey {
17        /// Public identifier; never the secret access key.
18        access_key_id: String,
19    },
20}
21
22impl MemberAccess {
23    /// Whether both name the same credential: one account, compared without
24    /// case or surrounding space, or one S3 key id.
25    pub fn same_credential(&self, other: &Self) -> bool {
26        match (self, other) {
27            (Self::ProviderAccount(a), Self::ProviderAccount(b)) => Self::same_account(a, b),
28            (Self::S3AccessKey { access_key_id: a }, Self::S3AccessKey { access_key_id: b }) => {
29                a == b
30            }
31            _ => false,
32        }
33    }
34
35    /// Whether two provider accounts are one: without case or surrounding space.
36    pub fn same_account(a: &str, b: &str) -> bool {
37        a.trim().eq_ignore_ascii_case(b.trim())
38    }
39
40    /// An account in the one spelling its equal spellings share: trimmed and
41    /// lowercase.
42    pub fn normalized_account(account: &str) -> String {
43        account.trim().to_ascii_lowercase()
44    }
45
46    /// Whether both are the same kind of access: provider accounts, or S3
47    /// keys.
48    pub fn same_kind(&self, other: &Self) -> bool {
49        matches!(
50            (self, other),
51            (Self::ProviderAccount(_), Self::ProviderAccount(_))
52                | (Self::S3AccessKey { .. }, Self::S3AccessKey { .. })
53        )
54    }
55}
56
57fn get_text(input: &mut Decoder<'_>) -> Result<String, Error> {
58    crate::wire::get_text(input, MAX_TEXT, "member access")
59}
60
61impl Wire for MemberAccess {
62    fn put(&self, out: &mut Encoder) -> Result<(), Error> {
63        match self {
64            Self::ProviderAccount(account) => {
65                crate::error::bound(account.len(), MAX_TEXT, "member access")?;
66                0u8.put(out)?;
67                account.put(out)
68            }
69            Self::S3AccessKey { access_key_id } => {
70                crate::error::bound(access_key_id.len(), MAX_TEXT, "member access")?;
71                1u8.put(out)?;
72                access_key_id.put(out)
73            }
74        }
75    }
76    fn get(input: &mut Decoder<'_>) -> Result<Self, Error> {
77        match u8::get(input)? {
78            0 => Ok(Self::ProviderAccount(get_text(input)?)),
79            1 => Ok(Self::S3AccessKey { access_key_id: get_text(input)? }),
80            tag => Err(Error::UnknownTag { field: "member access", tag }),
81        }
82    }
83}
84
85#[cfg(test)]
86#[path = "member_access_tests.rs"]
87mod tests;