use std::{
borrow::Cow,
collections::HashMap,
convert::TryFrom,
fmt::{Display, Formatter, Write},
hash::Hash,
};
use serde::{Deserialize, Serialize};
use super::{Action, ActionName};
#[derive(Clone, Debug, Serialize, Deserialize)]
#[must_use]
pub struct Statement {
pub resources: Vec<ResourceName<'static>>,
pub actions: Option<ActionNameList>,
pub configuration: Option<HashMap<String, Configuration>>,
}
impl Statement {
pub fn allow_all_for_any_resource() -> Self {
Self::for_any().allowing_all()
}
pub fn for_resource(name: impl Into<ResourceName<'static>>) -> Self {
Self {
resources: vec![name.into()],
actions: None,
configuration: None,
}
}
pub fn for_any() -> Self {
Self {
resources: vec![ResourceName::any()],
actions: None,
configuration: None,
}
}
pub fn for_resources<II: IntoIterator<Item = ResourceName<'static>>>(names: II) -> Self {
Self {
resources: names.into_iter().collect(),
actions: None,
configuration: None,
}
}
pub fn allow<A: Action>(&mut self, action: &A) {
match &mut self.actions {
Some(ActionNameList::All) => {}
Some(ActionNameList::List(names)) => {
names.push(action.name());
}
None => {
self.actions = Some(ActionNameList::List(vec![action.name()]));
}
}
}
pub fn allowing<A: Action>(mut self, action: &A) -> Self {
self.allow(action);
self
}
pub fn allow_all(&mut self) {
self.actions = Some(ActionNameList::All);
}
pub fn allowing_all(mut self) -> Self {
self.allow_all();
self
}
pub fn configure<S: Into<String>, C: Into<Configuration>>(&mut self, key: S, configuration: C) {
let configurations = self.configuration.get_or_insert_with(HashMap::default);
configurations.insert(key.into(), configuration.into());
}
pub fn with<S: Into<String>, C: Into<Configuration>>(
mut self,
key: S,
configuration: C,
) -> Self {
self.configure(key, configuration);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Identifier<'a> {
Any,
Integer(u64),
String(Cow<'a, str>),
Bytes(Cow<'a, [u8]>),
}
impl<'a> Hash for Identifier<'a> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
Identifier::Any => {
state.write_u8(0);
}
Identifier::Integer(int) => {
state.write(&int.to_be_bytes());
}
Identifier::String(string) => {
state.write(string.as_bytes());
}
Identifier::Bytes(bytes) => {
state.write(bytes);
}
}
}
}
#[test]
fn identifier_hash_tests() {
fn hash(identifier: &Identifier<'_>) -> u64 {
use std::hash::Hasher;
let mut hasher = std::collections::hash_map::DefaultHasher::default();
identifier.hash(&mut hasher);
hasher.finish()
}
let integer_a = Identifier::from(u64::from_be_bytes(*b"helloooo"));
let string_a = Identifier::from("helloooo");
let bytes_a = Identifier::from(b"helloooo");
let string_b = Identifier::from("woooorld");
assert_eq!(hash(&Identifier::Any), hash(&Identifier::Any));
assert_eq!(hash(&string_a), hash(&string_a));
assert_eq!(hash(&integer_a), hash(&string_a));
assert_eq!(hash(&bytes_a), hash(&string_a));
assert_ne!(hash(&string_a), hash(&string_b));
assert_ne!(hash(&integer_a), hash(&string_b));
assert_ne!(hash(&bytes_a), hash(&string_b));
}
impl<'a> Eq for Identifier<'a> {}
impl<'a> PartialEq for Identifier<'a> {
fn eq(&self, other: &Self) -> bool {
match other {
Self::Any => matches!(self, Self::Any),
Self::Integer(int) => self.eq_int(*int),
Self::String(string) => self.eq_str(string),
Self::Bytes(bytes) => self.eq_bytes(bytes),
}
}
}
impl<'a> Identifier<'a> {
#[must_use]
pub fn to_owned(&self) -> Identifier<'static> {
match self {
Self::Any => Identifier::Any,
Self::Integer(value) => Identifier::Integer(*value),
Self::String(value) => Identifier::String(Cow::Owned(value.to_string())),
Self::Bytes(value) => Identifier::Bytes(Cow::Owned(value.to_vec())),
}
}
fn eq_int(&self, other: u64) -> bool {
match self {
Identifier::Any => false,
Identifier::Integer(int) => *int == other,
Identifier::String(string) => {
let other = other.to_be_bytes();
string.as_bytes() == other
}
Identifier::Bytes(bytes) => {
let other = other.to_be_bytes();
**bytes == other
}
}
}
fn eq_str(&self, other: &str) -> bool {
match self {
Identifier::Any => false,
Identifier::Integer(int) => {
let int = int.to_be_bytes();
int == other.as_bytes()
}
Identifier::String(string) => string == other,
Identifier::Bytes(bytes) => &**bytes == other.as_bytes(),
}
}
fn eq_bytes(&self, other: &[u8]) -> bool {
match self {
Identifier::Any => false,
Identifier::Integer(int) => {
let int = int.to_be_bytes();
int == other
}
Identifier::String(string) => string.as_bytes() == other,
Identifier::Bytes(bytes) => &**bytes == other,
}
}
}
#[test]
fn identifier_equality_tests() {
let integer_a = Identifier::from(u64::from_be_bytes(*b"helloooo"));
let integer_b = Identifier::from(u64::from_be_bytes(*b"woooorld"));
let string_a = Identifier::from("helloooo");
let string_b = Identifier::from("woooorld");
let bytes_a = Identifier::from(b"helloooo");
let bytes_b = Identifier::from(b"woooorld");
assert_ne!(integer_a, Identifier::Any);
assert_eq!(integer_a, integer_a);
assert_eq!(integer_a, string_a);
assert_eq!(integer_a, bytes_a);
assert_ne!(integer_a, integer_b);
assert_ne!(integer_a, string_b);
assert_ne!(integer_a, bytes_b);
assert_ne!(string_a, Identifier::Any);
assert_eq!(string_a, integer_a);
assert_eq!(string_a, string_a);
assert_eq!(string_a, bytes_a);
assert_ne!(string_a, integer_b);
assert_ne!(string_a, string_b);
assert_ne!(string_a, bytes_b);
assert_ne!(bytes_a, Identifier::Any);
assert_eq!(bytes_a, integer_a);
assert_eq!(bytes_a, string_a);
assert_eq!(bytes_a, bytes_a);
assert_ne!(bytes_a, integer_b);
assert_ne!(bytes_a, string_b);
assert_ne!(bytes_a, bytes_b);
assert_eq!(Identifier::Any, Identifier::Any);
assert_ne!(Identifier::Any, integer_a);
assert_ne!(Identifier::Any, string_a);
assert_ne!(Identifier::Any, bytes_a);
}
impl<'a> Display for Identifier<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Any => f.write_char('*'),
Self::Integer(integer) => integer.fmt(f),
Self::String(string) => string.fmt(f),
Self::Bytes(bytes) => {
f.write_char('$')?;
for byte in bytes.iter() {
write!(f, "{:02x}", byte)?;
}
Ok(())
}
}
}
}
#[test]
fn identifier_display_tests() {
assert_eq!(Identifier::Any.to_string(), "*");
assert_eq!(Identifier::from(1).to_string(), "1");
assert_eq!(Identifier::from("string").to_string(), "string");
assert_eq!(Identifier::from(b"bytes").to_string(), "$6279746573");
}
impl<'a> From<u64> for Identifier<'a> {
fn from(id: u64) -> Self {
Self::Integer(id)
}
}
impl<'a> From<&'a str> for Identifier<'a> {
fn from(id: &'a str) -> Self {
Self::String(Cow::Borrowed(id))
}
}
impl<'a> From<&'a String> for Identifier<'a> {
fn from(id: &'a String) -> Self {
Self::from(id.as_str())
}
}
impl<'a> From<String> for Identifier<'a> {
fn from(id: String) -> Self {
Self::String(Cow::Owned(id))
}
}
impl<'a, const N: usize> From<&'a [u8; N]> for Identifier<'a> {
fn from(id: &'a [u8; N]) -> Self {
Self::from(&id[..])
}
}
impl<'a, const N: usize> From<[u8; N]> for Identifier<'a> {
fn from(id: [u8; N]) -> Self {
Self::from(id.to_vec())
}
}
impl<'a> From<&'a [u8]> for Identifier<'a> {
fn from(id: &'a [u8]) -> Self {
Self::Bytes(Cow::Borrowed(id))
}
}
impl<'a> From<&'a Vec<u8>> for Identifier<'a> {
fn from(id: &'a Vec<u8>) -> Self {
Self::from(id.clone())
}
}
impl<'a> From<Vec<u8>> for Identifier<'a> {
fn from(id: Vec<u8>) -> Self {
Self::Bytes(Cow::Owned(id))
}
}
#[test]
fn identifier_from_tests() {
assert_eq!(Identifier::from(1).to_string(), "1");
assert_eq!(Identifier::from("string").to_string(), "string");
assert_eq!(
Identifier::from(&String::from("string")).to_string(),
"string"
);
assert_eq!(
Identifier::from(String::from("string")).to_string(),
"string"
);
assert_eq!(Identifier::from(b"bytes").to_string(), "$6279746573");
assert_eq!(Identifier::from(*b"bytes").to_string(), "$6279746573");
assert_eq!(
Identifier::from(&b"bytes".to_vec()).to_string(),
"$6279746573"
);
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ActionNameList {
List(Vec<ActionName>),
All,
}
impl<T> From<T> for ActionNameList
where
T: Action,
{
fn from(action: T) -> Self {
Self::List(vec![action.name()])
}
}
impl<T> From<Vec<T>> for ActionNameList
where
T: Action,
{
fn from(actions: Vec<T>) -> Self {
Self::List(actions.into_iter().map(|action| action.name()).collect())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Configuration {
Unsigned(u64),
Signed(i64),
String(String),
}
impl Configuration {
#[must_use]
pub fn to_signed(&self) -> Option<i64> {
match self {
Configuration::Unsigned(unsigned) => i64::try_from(*unsigned).ok(),
Configuration::Signed(signed) => Some(*signed),
Configuration::String(string) => string.parse().ok(),
}
}
#[must_use]
pub fn to_unsigned(&self) -> Option<u64> {
match self {
Configuration::Unsigned(unsigned) => Some(*unsigned),
Configuration::Signed(signed) => u64::try_from(*signed).ok(),
Configuration::String(string) => string.parse().ok(),
}
}
}
impl Display for Configuration {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Configuration::Unsigned(unsigned) => unsigned.fmt(f),
Configuration::Signed(signed) => signed.fmt(f),
Configuration::String(string) => string.fmt(f),
}
}
}
impl From<u64> for Configuration {
fn from(value: u64) -> Self {
Self::Unsigned(value)
}
}
impl From<i64> for Configuration {
fn from(value: i64) -> Self {
Self::Signed(value)
}
}
impl From<String> for Configuration {
fn from(value: String) -> Self {
Self::String(value)
}
}
impl<'a> From<&'a str> for Configuration {
fn from(value: &'a str) -> Self {
Self::String(value.to_string())
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct ResourceName<'a>(Vec<Identifier<'a>>);
impl<'a> ResourceName<'a> {
#[must_use]
pub fn to_owned(&self) -> ResourceName<'static> {
ResourceName(self.0.iter().map(Identifier::to_owned).collect())
}
}
impl<'a> Display for ResourceName<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
for (index, identifier) in self.0.iter().enumerate() {
if index > 0 {
f.write_char('.')?;
}
identifier.fmt(f)?;
}
Ok(())
}
}
impl<'a> ResourceName<'a> {
#[must_use]
pub fn any() -> Self {
Self::named(Identifier::Any)
}
#[must_use]
pub fn named<I: Into<Identifier<'a>>>(name: I) -> Self {
Self(vec![name.into()])
}
#[must_use]
pub fn and<I: Into<Identifier<'a>>>(mut self, name: I) -> Self {
self.0.push(name.into());
self
}
}
impl<'a> AsRef<[Identifier<'a>]> for ResourceName<'a> {
fn as_ref(&self) -> &[Identifier<'a>] {
&self.0
}
}
impl<'a> IntoIterator for ResourceName<'a> {
type IntoIter = std::vec::IntoIter<Identifier<'a>>;
type Item = Identifier<'a>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'b, 'a> From<&'b [Identifier<'a>]> for ResourceName<'a> {
fn from(parts: &'b [Identifier<'a>]) -> Self {
Self(parts.to_vec())
}
}
impl<'a> From<&'a str> for ResourceName<'a> {
fn from(name: &'a str) -> Self {
Self(vec![Identifier::from(name)])
}
}
impl<'a> From<u64> for ResourceName<'a> {
fn from(name: u64) -> Self {
Self(vec![Identifier::from(name)])
}
}