use std::collections::{hash_map, HashMap, HashSet};
use serde::{Deserialize, Serialize};
use crate::proto::{
data_model::Entry,
grouping::{self, Area, AreaExt, AreaOfInterest, Point},
keys::{NamespaceId, UserId},
meadowcap::{self, AccessMode, McCapability, ReadAuthorisation},
};
pub type InterestMap = HashMap<ReadAuthorisation, HashSet<AreaOfInterest>>;
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub enum Interests {
#[default]
All,
Select(HashMap<CapSelector, AreaOfInterestSelector>),
}
impl Interests {
pub fn builder() -> InterestBuilder {
InterestBuilder::default()
}
pub fn all() -> Self {
Self::All
}
}
#[derive(Default, Debug)]
pub struct InterestBuilder(HashMap<CapSelector, AreaOfInterestSelector>);
pub trait IntoAreaOfInterest {
fn into_area_of_interest(self) -> AreaOfInterest;
}
impl IntoAreaOfInterest for AreaOfInterest {
fn into_area_of_interest(self) -> AreaOfInterest {
self
}
}
impl IntoAreaOfInterest for Area {
fn into_area_of_interest(self) -> AreaOfInterest {
AreaOfInterest::new(self, 0, 0)
}
}
impl InterestBuilder {
pub fn add(mut self, cap: impl Into<CapSelector>, areas: AreaOfInterestSelector) -> Self {
let cap = cap.into();
self.0.insert(cap, areas);
self
}
pub fn add_full_cap(self, cap: impl Into<CapSelector>) -> Self {
self.add(cap, AreaOfInterestSelector::Widest)
}
pub fn add_area(
mut self,
cap: impl Into<CapSelector>,
aois: impl IntoIterator<Item = impl IntoAreaOfInterest>,
) -> Self {
let cap = cap.into();
let aois = aois.into_iter();
let aois = aois.map(|aoi| aoi.into_area_of_interest());
match self.0.entry(cap) {
hash_map::Entry::Vacant(entry) => {
entry.insert(AreaOfInterestSelector::Exact(aois.collect()));
}
hash_map::Entry::Occupied(mut entry) => match entry.get_mut() {
AreaOfInterestSelector::Widest => {}
AreaOfInterestSelector::Exact(existing) => existing.extend(aois),
},
}
self
}
pub fn build(self) -> Interests {
Interests::Select(self.0)
}
}
impl From<InterestBuilder> for Interests {
fn from(builder: InterestBuilder) -> Self {
builder.build()
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub enum AreaOfInterestSelector {
#[default]
Widest,
#[serde(with = "serde_area_of_interest_set")]
Exact(HashSet<AreaOfInterest>),
}
mod serde_area_of_interest_set {
use serde::Deserializer;
use super::*;
use crate::proto::grouping::serde_encoding::SerdeAreaOfInterest;
pub fn serialize<S: serde::Serializer>(
items: &HashSet<AreaOfInterest>,
serializer: S,
) -> Result<S::Ok, S::Error> {
let items: Vec<_> = items
.iter()
.map(|aoi| SerdeAreaOfInterest(aoi.clone()))
.collect();
items.serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<HashSet<AreaOfInterest>, D::Error>
where
D: Deserializer<'de>,
{
let items: Vec<SerdeAreaOfInterest> = Deserialize::deserialize(deserializer)?;
Ok(items.into_iter().map(|aoi| aoi.0).collect())
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct CapSelector {
pub namespace_id: NamespaceId,
pub receiver: UserSelector,
pub granted_area: AreaSelector,
}
impl From<NamespaceId> for CapSelector {
fn from(value: NamespaceId) -> Self {
Self::any(value)
}
}
impl CapSelector {
pub fn is_covered_by(&self, cap: &McCapability) -> bool {
self.namespace_id == *cap.granted_namespace()
&& self.receiver.includes(cap.receiver())
&& self.granted_area.is_covered_by(&cap.granted_area())
}
pub fn new(
namespace_id: NamespaceId,
receiver: UserSelector,
granted_area: AreaSelector,
) -> Self {
Self {
namespace_id,
receiver,
granted_area,
}
}
pub fn with_user(namespace_id: NamespaceId, user_id: UserId) -> Self {
Self::new(
namespace_id,
UserSelector::Exact(user_id),
AreaSelector::Widest,
)
}
pub fn any(namespace: NamespaceId) -> Self {
Self::new(namespace, UserSelector::Any, AreaSelector::Widest)
}
pub fn for_entry(entry: &Entry, user_id: UserSelector) -> Self {
let granted_area = AreaSelector::ContainsPoint(Point::from_entry(entry));
Self {
namespace_id: *entry.namespace_id(),
receiver: user_id,
granted_area,
}
}
}
#[derive(
Debug, Default, Clone, Copy, Eq, PartialEq, derive_more::From, Serialize, Deserialize, Hash,
)]
pub enum UserSelector {
#[default]
Any,
Exact(UserId),
}
impl UserSelector {
pub fn includes(&self, user: &UserId) -> bool {
match self {
Self::Any => true,
Self::Exact(u) => u == user,
}
}
}
#[derive(Debug, Clone, Default, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub enum AreaSelector {
#[default]
Widest,
ContainsArea(#[serde(with = "grouping::serde_encoding::area")] Area),
ContainsPoint(Point),
}
impl AreaSelector {
pub fn is_covered_by(&self, other: &Area) -> bool {
match self {
AreaSelector::Widest => true,
AreaSelector::ContainsArea(area) => other.includes_area(area),
AreaSelector::ContainsPoint(point) => other.includes_point(point),
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum CapabilityPack {
Read(#[serde(with = "meadowcap::serde_encoding::read_authorisation")] ReadAuthorisation),
Write(#[serde(with = "meadowcap::serde_encoding::mc_capability")] McCapability),
}
impl CapabilityPack {
pub fn receiver(&self) -> UserId {
match self {
CapabilityPack::Read(auth) => *auth.read_cap().receiver(),
CapabilityPack::Write(cap) => *cap.receiver(),
}
}
pub fn namespace(&self) -> NamespaceId {
match self {
CapabilityPack::Read(cap) => cap.namespace(),
CapabilityPack::Write(cap) => *cap.granted_namespace(),
}
}
pub fn validate(&self) -> Result<(), InvalidCapabilityPack> {
let is_valid = match self {
Self::Read(cap) => cap.read_cap().access_mode() == AccessMode::Read,
Self::Write(cap) => cap.access_mode() == AccessMode::Write,
};
if !is_valid {
Err(InvalidCapabilityPack)
} else {
Ok(())
}
}
}
#[derive(Debug, thiserror::Error)]
#[error("Invalid capability pack.")]
pub struct InvalidCapabilityPack;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DelegateTo {
pub user: UserId,
pub restrict_area: RestrictArea,
}
impl DelegateTo {
pub fn new(user: UserId, restrict_area: RestrictArea) -> Self {
Self {
user,
restrict_area,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub enum RestrictArea {
#[default]
None,
Restrict(#[serde(with = "grouping::serde_encoding::area")] Area),
}
impl RestrictArea {
pub fn or_default(self, default: Area) -> Area {
match self {
RestrictArea::None => default.clone(),
RestrictArea::Restrict(area) => area,
}
}
}