use std::collections::HashMap;
use matrix_sdk::{ruma::events::tag::TagInfo, Room};
pub async fn get_tags(room: &Room, namespace: &str) -> Vec<String> {
let mut all_tags = Vec::new();
let tags = room.tags().await.unwrap_or_default();
for (tag, _) in tags.unwrap_or_default() {
if tag.to_string().starts_with(namespace) {
let tag = tag.to_string();
let tag = tag.replacen(&namespace.to_string(), "", 1);
let tag = tag.trim_start_matches('.');
all_tags.push(tag.to_string());
}
}
all_tags
}
pub async fn add_tag(room: &Room, namespace: &str, tag: &str) -> Result<(), matrix_sdk::Error> {
if !namespace.is_empty() {
room.set_tag(format!("{}.{}", namespace, tag).into(), TagInfo::default())
.await?;
} else {
room.set_tag(tag.to_string().into(), TagInfo::default())
.await?;
}
Ok(())
}
pub async fn remove_tag(room: &Room, namespace: &str, tag: &str) -> Result<(), matrix_sdk::Error> {
if !namespace.is_empty() {
room.remove_tag(format!("{}.{}", namespace, tag).into())
.await?;
} else {
room.remove_tag(tag.to_string().into()).await?;
}
Ok(())
}
pub async fn replace_tags(room: &Room, namespace: &str, tags: &[String]) {
let mut existing_tags = get_tags(room, namespace).await;
let mut tags = tags.to_owned();
existing_tags.retain(|tag| !tags.contains(tag));
tags.retain(|tag| !existing_tags.contains(tag));
for tag in tags {
add_tag(room, namespace, &tag).await.unwrap();
}
for tag in existing_tags {
remove_tag(room, namespace, &tag).await.unwrap();
}
}
pub struct Tags<'a> {
namespace: String,
tags: Vec<String>,
room: &'a Room,
dirty: bool,
}
impl<'a> Tags<'a> {
pub async fn new(room: &'a Room, namespace: &str) -> Self {
let tags = get_tags(room, namespace).await;
Self {
namespace: namespace.to_string(),
tags,
room,
dirty: false,
}
}
pub fn add(&mut self, tag: &str) {
self.tags.push(tag.to_string());
self.dirty = true;
}
pub fn get_value(&self, key: &str) -> Option<String> {
for tag in &self.tags {
if tag.starts_with(&format!("{}=", key)) {
return Some(tag.split('=').nth(1)?.to_string());
}
}
None
}
pub fn add_kv(&mut self, key: &str, value: &str) {
self.tags.push(format!("{}={}", key, value));
self.dirty = true;
}
pub fn replace_kv(&mut self, key: &str, value: &str) {
self.tags.retain(|t| !t.starts_with(&format!("{}=", key)));
self.tags.push(format!("{}={}", key, value));
self.dirty = true;
}
pub fn remove_kv(&mut self, key: &str) {
self.tags.retain(|t| !t.starts_with(&format!("{}=", key)));
self.dirty = true;
}
pub fn remove(&mut self, tag: &str) {
self.tags.retain(|t| t != tag);
self.dirty = true;
}
pub async fn sync(&mut self) {
replace_tags(self.room, &self.namespace, &self.tags).await;
self.dirty = false;
}
pub fn namespace(&self) -> &str {
&self.namespace
}
pub fn tags(&self) -> &Vec<String> {
&self.tags
}
pub fn get_kvs(&self) -> HashMap<String, String> {
let mut kvs = HashMap::new();
for tag in &self.tags {
if let Some((key, value)) = tag.split_once('=') {
kvs.insert(key.to_string(), value.to_string());
}
}
kvs
}
pub fn is_dirty(&self) -> bool {
self.dirty
}
}
#[allow(unused)]
impl<'a> Drop for Tags<'a> {
fn drop(&mut self) {
if self.dirty {
let room = self.room.clone();
let namespace = self.namespace.clone();
let tags = self.tags.clone();
tokio::spawn(async move {
replace_tags(&room, &namespace, &tags).await;
});
}
}
}