use toml_edit::
{
Item,
Table,
Value,
};
use crate::
{
colors,
consts,
misc,
role::Role,
network::codes::
{
MessageColors,
UserProfile,
},
};
const COLOR_KEYS: [&str; 2] = ["username_color", "message_color"]; const PROFILE_TABLE: &str = "profile"; const PROFILE_KEYS: [&str; 4] = UserProfile::KEYS; const AVATAR_KEY: &str = "avatar";
fn user_field(username: &str, key: &str) -> Option<String> {
super::with_cached(&super::config_path(consts::SERVER_USERS_CONFIG), |users| users.get(username)?
.as_table_like()?.get(key)?.as_str().map(str::to_string))
}
fn profile_field(username: &str, key: &str) -> Option<String> {
super::with_cached(&super::config_path(consts::SERVER_USERS_CONFIG), |users| users.get(username)?
.as_table_like()?.get(PROFILE_TABLE)?.as_table_like()?.get(key)?.as_str().map(str::to_string))
}
fn write_user_field(username: &str, key: &str, value: Value) {
super::with_cached_mut(&super::config_path(consts::SERVER_USERS_CONFIG), |doc|
{
let users = doc.as_table_mut();
if users.get(username).and_then(Item::as_table_like).is_none()
{
users.insert(username, Item::Table(Table::new()));
}
users.get_mut(username).and_then(Item::as_table_like_mut)
.expect("User entry is not a table").insert(key, Item::Value(value));
});
}
fn write_profile_field(username: &str, key: &str, value: Value) {
super::with_cached_mut(&super::config_path(consts::SERVER_USERS_CONFIG), |doc|
{
let users = doc.as_table_mut();
if users.get(username).and_then(Item::as_table_like).is_none()
{
users.insert(username, Item::Table(Table::new()));
}
let user = users.get_mut(username).and_then(Item::as_table_like_mut).expect("User entry is not a table");
if user.get(PROFILE_TABLE).and_then(Item::as_table_like).is_none()
{
user.insert(PROFILE_TABLE, Item::Table(Table::new()));
}
user.get_mut(PROFILE_TABLE).and_then(Item::as_table_like_mut)
.expect("Profile entry is not a table").insert(key, Item::Value(value));
});
}
pub fn len() -> usize {
super::with_cached(&super::config_path(consts::SERVER_USERS_CONFIG), |users| users.len())
}
pub fn all() -> Vec<String> {
super::with_cached(&super::config_path(consts::SERVER_USERS_CONFIG), |users| users.iter()
.map(|(username, _)| username.to_string()).collect())
}
pub fn password(username: &str) -> Option<String> {
user_field(username, "password")
}
pub fn role(username: &str) -> Option<Role> {
user_field(username, "role")?.parse().ok()
}
pub fn colors(username: &str) -> MessageColors {
let mut codes = COLOR_KEYS.iter().map(|key| user_field(username, key).as_deref().and_then(colors::code));
MessageColors
{
username_color: codes.next().flatten(),
message_color: codes.next().flatten(),
}
}
pub fn profile(username: &str) -> UserProfile {
let mut fields = PROFILE_KEYS.iter().map(|key| profile_field(username, key).unwrap_or_default());
UserProfile
{
bio: fields.next().unwrap_or_default(),
pronouns: fields.next().unwrap_or_default(),
website: fields.next().unwrap_or_default(),
status: fields.next().unwrap_or_default(),
avatar: avatar(username),
}
}
pub fn avatar(username: &str) -> Option<[u8; 32]> {
misc::unhex(&profile_field(username, AVATAR_KEY)?)
}
pub fn set_avatar(username: &str, hash: Option<&[u8; 32]>) {
write_profile_field(username, AVATAR_KEY, hash.map(|hash| misc::hex(hash)).unwrap_or_default().into());
}
pub fn names_avatar(hash: &[u8; 32]) -> bool {
all().iter().any(|username| avatar(username).as_ref() == Some(hash))
}
pub fn avatars() -> Vec<[u8; 32]> {
all().iter().filter_map(|username| avatar(username)).collect()
}
pub fn set_profile(username: &str, profile: &UserProfile)
{
for (key, value) in PROFILE_KEYS.iter().zip(profile.fields())
{
write_profile_field(username, key, value.into());
}
}
pub fn set_color(username: &str, username_color: bool, code: u8)
{
let key = COLOR_KEYS[usize::from(!username_color)];
write_user_field(username, key, colors::name(Some(code)).into());
}
pub fn set_role(username: &str, role: Role) {
write_user_field(username, "role", role.name().into());
}
pub fn add(username: &str, hash: &str) -> bool {
let first_user = len() == 0;
write_user_field(username, "password", hash.into()); set_role(username, if first_user { Role::Owner } else { Role::User });
for key in COLOR_KEYS { write_user_field(username, key, colors::NONE.into()); }
for key in PROFILE_KEYS { write_profile_field(username, key, "".into()); }
write_profile_field(username, AVATAR_KEY, "".into());
first_user
}
pub fn contains(key: &str) -> bool {
super::with_cached(&super::config_path(consts::SERVER_USERS_CONFIG), |users| users.get(key).is_some())
}