use std::{
collections::{HashMap, HashSet},
mem,
};
use serde::{Deserialize, Serialize};
use crate::{
channel::{Channel, Message},
check_name_validity, check_permission,
error::{DataError, Error},
get_system_millis, new_id,
permission::{
ChannelPermission, ChannelPermissions, HubPermission, HubPermissions, PermissionSetting,
},
user::User,
Result, ID,
};
pub const HUB_INFO_FOLDER: &str = "data/hubs/info/";
pub const HUB_DATA_FOLDER: &str = "data/hubs/data/";
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct HubMember {
pub user: ID,
pub joined: u128,
pub hub: ID,
pub nickname: String,
pub groups: Vec<ID>,
pub hub_permissions: HubPermissions,
pub channel_permissions: HashMap<ID, ChannelPermissions>,
}
impl HubMember {
pub fn new(user: &User, hub: ID) -> Self {
Self {
nickname: user.username.clone(),
user: user.id.clone(),
hub,
groups: Vec::new(),
joined: get_system_millis(),
hub_permissions: HashMap::new(),
channel_permissions: HashMap::new(),
}
}
pub fn set_nickname(&mut self, nickname: String) -> Result<()> {
check_name_validity(&nickname)?;
self.nickname = nickname;
Ok(())
}
pub fn join_group(&mut self, group: &mut PermissionGroup) {
if !self.groups.contains(&group.id) {
self.groups.push(group.id.clone());
}
if !group.members.contains(&self.user) {
group.members.push(self.user.clone());
}
}
pub fn leave_group(&mut self, group: &mut PermissionGroup) {
if let Some(index) = self.groups.iter().position(|id| id == &group.id) {
self.groups.remove(index);
}
if let Some(index) = group.members.iter().position(|id| id == &self.user) {
group.members.remove(index);
}
}
pub fn set_permission(&mut self, permission: HubPermission, value: PermissionSetting) {
self.hub_permissions.insert(permission, value);
}
pub fn set_channel_permission(
&mut self,
channel: &ID,
permission: ChannelPermission,
value: PermissionSetting,
) {
let channel_permissions = self
.channel_permissions
.entry(*channel)
.or_insert(HashMap::new());
channel_permissions.insert(permission, value);
}
pub fn has_all_permissions(&self) -> bool {
if let Some(value) = self.hub_permissions.get(&HubPermission::All) {
if value == &Some(true) {
return true;
}
}
false
}
pub fn has_permission(&self, permission: HubPermission, hub: &Hub) -> bool {
if hub.owner == self.user {
return true;
}
if self.has_all_permissions() {
return true;
}
if let Some(value) = self.hub_permissions.get(&permission) {
match value {
&Some(true) => {
return true;
}
&Some(false) => {
return false;
}
None => {}
};
} else {
for group in self.groups.iter() {
if let Some(group) = hub.groups.get(&group) {
if group.has_permission(&permission) {
return true;
}
}
}
}
false
}
pub fn has_channel_permission(
&self,
channel: &ID,
permission: &ChannelPermission,
hub: &Hub,
) -> bool {
if hub.owner == self.user {
return true;
}
if self.has_all_permissions() {
return true;
}
if let Some(channel) = self.channel_permissions.get(channel) {
if let Some(value) = channel.get(&ChannelPermission::All) {
if value == &Some(true) {
return true;
}
}
if let Some(value) = channel.get(permission) {
match value {
&Some(true) => {
return true;
}
&Some(false) => {
return false;
}
None => {
if self.has_permission(permission.hub_equivalent(), hub) {
return true;
}
}
};
}
} else {
for group in self.groups.iter() {
if let Some(group) = hub.groups.get(&group) {
if group.has_channel_permission(channel, permission) {
return true;
}
}
}
}
false
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct PermissionGroup {
pub id: ID,
pub name: String,
pub members: Vec<ID>,
pub hub_permissions: HubPermissions,
pub channel_permissions: HashMap<ID, ChannelPermissions>,
pub created: u128,
}
impl PermissionGroup {
pub fn new(name: String, id: ID) -> Self {
Self {
created: get_system_millis(),
id,
name,
members: Vec::new(),
hub_permissions: HashMap::new(),
channel_permissions: HashMap::new(),
}
}
pub fn add_member(&mut self, user: &mut HubMember) {
user.join_group(self)
}
pub fn remove_member(&mut self, user: &mut HubMember) {
user.leave_group(self)
}
pub fn set_permission(&mut self, permission: HubPermission, value: PermissionSetting) {
self.hub_permissions.insert(permission, value);
}
pub fn set_channel_permission(
&mut self,
channel_id: ID,
permission: ChannelPermission,
value: PermissionSetting,
) {
let channel_permissions = self
.channel_permissions
.entry(channel_id)
.or_insert(HashMap::new());
channel_permissions.insert(permission, value);
}
pub fn has_all_permissions(&self) -> bool {
if let Some(value) = self.hub_permissions.get(&HubPermission::All) {
if value == &Some(true) {
return true;
}
}
false
}
pub fn has_permission(&self, permission: &HubPermission) -> bool {
if self.has_all_permissions() {
return true;
}
if let Some(value) = self.hub_permissions.get(permission) {
if value == &Some(true) {
return true;
}
}
return false;
}
pub fn has_channel_permission(&self, channel_id: &ID, permission: &ChannelPermission) -> bool {
if self.has_all_permissions() {
return true;
}
if let Some(channel) = self.channel_permissions.get(channel_id) {
if let Some(value) = channel.get(&ChannelPermission::All) {
if value == &Some(true) {
return true;
}
}
if let Some(value) = channel.get(&permission) {
if value == &Some(true) {
return true;
} else if value == &None {
if self.has_permission(&permission.hub_equivalent()) {
return true;
}
}
}
}
return false;
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct Hub {
pub channels: HashMap<ID, Channel>,
pub members: HashMap<ID, HubMember>,
pub bans: HashSet<ID>,
pub mutes: HashSet<ID>,
pub description: String,
pub owner: ID,
pub groups: HashMap<ID, PermissionGroup>,
pub default_group: ID,
pub name: String,
pub id: ID,
pub created: u128,
}
impl Hub {
pub fn new(name: String, id: ID, creator: &User) -> Self {
let creator_id = creator.id.clone();
let mut everyone = PermissionGroup::new(String::from("everyone"), new_id());
let mut owner = HubMember::new(creator, id.clone());
let mut members = HashMap::new();
let mut groups = HashMap::new();
owner.join_group(&mut everyone);
owner.set_permission(HubPermission::All, Some(true));
members.insert(creator_id.clone(), owner);
groups.insert(everyone.id.clone(), everyone.clone());
Self {
name,
id,
groups,
description: String::new(),
default_group: everyone.id.clone(),
owner: creator_id,
bans: HashSet::new(),
mutes: HashSet::new(),
channels: HashMap::new(),
members,
created: get_system_millis(),
}
}
pub async fn new_channel(&mut self, member_id: &ID, name: String) -> Result<ID> {
check_name_validity(&name)?;
let member = self.get_member(member_id)?;
check_permission!(member, HubPermission::CreateChannel, self);
let mut id = new_id();
while self.channels.contains_key(&id) {
id = new_id();
}
let channel = Channel::new(name, id.clone(), self.id.clone());
channel.create_dir().await?;
{
self.get_member_mut(member_id)?.set_channel_permission(
&channel.id,
ChannelPermission::ViewChannel,
Some(true),
);
}
self.channels.insert(id.clone(), channel);
Ok(id)
}
pub fn get_channel(&self, member_id: &ID, channel_id: &ID) -> Result<&Channel> {
let member = self.get_member(member_id)?;
check_permission!(member, channel_id, ChannelPermission::ViewChannel, self);
if let Some(channel) = self.channels.get(channel_id) {
Ok(channel)
} else {
Err(Error::ChannelNotFound)
}
}
pub fn get_channel_mut(&mut self, member_id: &ID, channel_id: &ID) -> Result<&mut Channel> {
let member = self.get_member(member_id)?;
check_permission!(member, channel_id, ChannelPermission::ViewChannel, self);
if let Some(channel) = self.channels.get_mut(channel_id) {
Ok(channel)
} else {
Err(Error::ChannelNotFound)
}
}
pub fn get_member(&self, member_id: &ID) -> Result<HubMember> {
if let Some(member) = self.members.get(member_id) {
Ok(member.clone())
} else {
Err(Error::MemberNotFound)
}
}
pub fn get_member_mut(&mut self, member_id: &ID) -> Result<&mut HubMember> {
if let Some(member) = self.members.get_mut(member_id) {
Ok(member)
} else {
Err(Error::MemberNotFound)
}
}
pub async fn change_channel_description(
&mut self,
user_id: &ID,
channel_id: &ID,
new_description: String,
) -> Result<String> {
if new_description.as_bytes().len() > crate::MAX_DESCRIPTION_SIZE {
Err(Error::TooBig)
} else {
if let Some(user) = self.members.get(user_id) {
check_permission!(user, channel_id, ChannelPermission::ViewChannel, self);
check_permission!(user, channel_id, ChannelPermission::Configure, self);
if let Some(channel) = self.channels.get_mut(channel_id) {
Ok(mem::replace(&mut channel.description, new_description))
} else {
Err(Error::ChannelNotFound)
}
} else {
Err(Error::NotInHub)
}
}
}
pub async fn rename_channel(
&mut self,
user_id: &ID,
channel_id: &ID,
new_name: String,
) -> Result<String> {
check_name_validity(&new_name)?;
if let Some(user) = self.members.get(user_id) {
check_permission!(user, channel_id, ChannelPermission::ViewChannel, self);
check_permission!(user, channel_id, ChannelPermission::Configure, self);
if let Some(channel) = self.channels.get_mut(channel_id) {
Ok(mem::replace(&mut channel.name, new_name))
} else {
Err(Error::ChannelNotFound)
}
} else {
Err(Error::NotInHub)
}
}
pub async fn delete_channel(&mut self, user_id: &ID, channel_id: &ID) -> Result<()> {
if let Some(user) = self.members.get(user_id) {
check_permission!(user, HubPermission::DeleteChannel, self);
check_permission!(user, channel_id, ChannelPermission::ViewChannel, self);
if let Some(_) = self.channels.remove(channel_id) {
Ok(())
} else {
Err(Error::ChannelNotFound)
}
} else {
Err(Error::NotInHub)
}
}
pub async fn send_message(
&mut self,
user_id: &ID,
channel_id: &ID,
message: String,
) -> Result<Message> {
if let Some(member) = self.members.get(&user_id) {
if !self.mutes.contains(&user_id) {
check_permission!(member, channel_id, ChannelPermission::ViewChannel, self);
check_permission!(member, channel_id, ChannelPermission::SendMessage, self);
if let Some(channel) = self.channels.get(&channel_id) {
let message = Message {
id: new_id(),
sender: member.user.clone(),
created: get_system_millis(),
content: message,
};
channel.add_message(message.clone()).await?;
Ok(message)
} else {
Err(Error::ChannelNotFound)
}
} else {
Err(Error::Muted)
}
} else {
Err(Error::NotInHub)
}
}
pub fn get_info_path(&self) -> String {
format!("{}{:x}", HUB_INFO_FOLDER, self.id.as_u128())
}
pub fn get_data_path(&self) -> String {
format!("{}{:x}/", HUB_DATA_FOLDER, self.id.as_u128())
}
pub async fn save(&self) -> Result<()> {
tokio::fs::create_dir_all(HUB_INFO_FOLDER).await?;
tokio::fs::write(
self.get_info_path(),
&bincode::serialize(self).map_err(|_| DataError::Serialize)?,
)
.await?;
Ok(())
}
pub async fn load(id: &ID) -> Result<Self> {
let filename = format!("{}{:x}", HUB_INFO_FOLDER, id.as_u128());
let path = std::path::Path::new(&filename);
if !path.exists() {
return Err(Error::HubNotFound);
}
bincode::deserialize(&tokio::fs::read(path).await?).map_err(|_| DataError::Deserialize)?
}
pub fn user_join(&mut self, user: &User) -> Result<HubMember> {
let mut member = HubMember::new(user, self.id.clone());
if let Some(group) = self.groups.get_mut(&self.default_group) {
group.add_member(&mut member);
self.members.insert(member.user.clone(), member.clone());
Ok(member)
} else {
Err(Error::GroupNotFound)
}
}
pub fn user_leave(&mut self, user: &User) -> Result<()> {
if let Some(member) = self.members.get_mut(&user.id) {
if let Some(group) = self.groups.get_mut(&self.default_group) {
member.leave_group(group);
self.members.remove(&user.id);
Ok(())
} else {
Err(Error::GroupNotFound)
}
} else {
Err(Error::NotInHub)
}
}
pub async fn kick_user(&mut self, user_id: &ID) -> Result<()> {
if self.members.contains_key(user_id) {
let mut user = User::load(user_id).await?;
self.user_leave(&user)?;
if let Some(index) = user.in_hubs.iter().position(|id| id == &self.id) {
user.in_hubs.remove(index);
}
user.save().await?;
self.members.remove(&user_id);
}
Ok(())
}
pub async fn ban_user(&mut self, user_id: ID) -> Result<()> {
self.kick_user(&user_id).await?;
self.bans.insert(user_id);
Ok(())
}
pub fn unban_user(&mut self, user_id: &ID) {
self.bans.remove(user_id);
}
pub fn mute_user(&mut self, user_id: ID) {
self.mutes.insert(user_id);
}
pub fn unmute_user(&mut self, user_id: &ID) {
self.mutes.remove(user_id);
}
pub fn get_channels_for_user(&self, user_id: &ID) -> Result<HashMap<ID, Channel>> {
let hub_im = self.clone();
if let Some(user) = self.members.get(user_id) {
let mut result = HashMap::new();
for channel in self.channels.clone() {
if user.has_channel_permission(&channel.0, &ChannelPermission::ViewChannel, &hub_im)
{
result.insert(channel.0.clone(), channel.1.clone());
}
}
Ok(result)
} else {
Err(Error::MemberNotFound)
}
}
pub fn strip(&self, user_id: &ID) -> Result<Self> {
let mut hub = self.clone();
hub.channels = self.get_channels_for_user(user_id)?;
Ok(hub)
}
}