basic creation
This commit is contained in:
parent
8b072325d8
commit
e15b2b407a
14 changed files with 1258 additions and 8 deletions
212
src/util/chatFiles.rs
Normal file
212
src/util/chatFiles.rs
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
|
||||
use json::{self, Value};
|
||||
use std::fs::{self, File};
|
||||
use std::io::{Read, Write};
|
||||
use std::path::Path;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum MessageState {
|
||||
Read,
|
||||
Received,
|
||||
Sending,
|
||||
Error,
|
||||
}
|
||||
|
||||
impl MessageState {
|
||||
fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
MessageState::Read => "READ",
|
||||
MessageState::Received => "RECEIVED",
|
||||
MessageState::Sending => "SENDING",
|
||||
MessageState::Error => "ERROR",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ChatFiles;
|
||||
|
||||
impl ChatFiles {
|
||||
fn load_file(dir: &str, file_name: &str) -> String {
|
||||
let path = Path::new(dir).join(file_name);
|
||||
if let Ok(mut f) = File::open(&path) {
|
||||
let mut content = String::new();
|
||||
let _ = f.read_to_string(&mut content);
|
||||
content
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn save_file(dir: &str, file_name: &str, content: &str) -> std::io::Result<()> {
|
||||
fs::create_dir_all(dir)?;
|
||||
let path = Path::new(dir).join(file_name);
|
||||
let mut file = File::create(path)?;
|
||||
file.write_all(content.as_bytes())
|
||||
}
|
||||
|
||||
pub fn add_message(
|
||||
send_time: i64,
|
||||
storage_owner_is_sender: bool,
|
||||
storage_owner: Uuid,
|
||||
external_user: Uuid,
|
||||
message: &str,
|
||||
) -> std::io::Result<()> {
|
||||
let user_dir = format!("users/{}/chats/{}", storage_owner, external_user);
|
||||
|
||||
let mut chunk_index = 0;
|
||||
let mut message_chunk = array![];
|
||||
|
||||
// find latest chunk not full (max 800 msgs)
|
||||
loop {
|
||||
let file_name = format!("msgs_{}.json", chunk_index);
|
||||
let file_content = Self::load_file(&user_dir, &file_name);
|
||||
|
||||
if !file_content.is_empty() {
|
||||
if let Ok(current_chunk) = json::parse(&file_content) {
|
||||
if current_chunk.len() < 800 {
|
||||
message_chunk = current_chunk;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
chunk_index += 1;
|
||||
}
|
||||
|
||||
let json_obj = object! {
|
||||
"message_time" => send_time,
|
||||
"message_content" => message,
|
||||
"sender_is_me" => storage_owner_is_sender,
|
||||
"message_state" => MessageState::Sending.as_str()
|
||||
};
|
||||
message_chunk.push(json_obj).unwrap();
|
||||
|
||||
Self::save_file(&user_dir, &format!("msgs_{}.json", chunk_index), &message_chunk.dump())
|
||||
}
|
||||
|
||||
pub fn change_message_state(
|
||||
storage_owner: Uuid,
|
||||
external_user: Uuid,
|
||||
timestamp: i64,
|
||||
new_state: MessageState,
|
||||
) -> std::io::Result<()> {
|
||||
let user_dir = format!("users/{}/chats/{}", storage_owner, external_user);
|
||||
let path = Path::new(&user_dir);
|
||||
|
||||
if !path.exists() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let entries = fs::read_dir(path)?;
|
||||
for entry in entries {
|
||||
let entry = entry?;
|
||||
let fname = entry.file_name();
|
||||
let fname_str = fname.to_string_lossy();
|
||||
|
||||
if fname_str.starts_with("msgs_") && fname_str.ends_with(".json") {
|
||||
let file_content = Self::load_file(&user_dir, &fname_str);
|
||||
if file_content.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Ok(mut chunk) = json::parse(&file_content) {
|
||||
let mut modified = false;
|
||||
for i in 0..chunk.len() {
|
||||
if chunk[i]["message_time"].as_i64() == Some(timestamp) {
|
||||
chunk[i]["message_state"] = JsonValue::from(new_state.as_str());
|
||||
modified = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if modified {
|
||||
Self::save_file(&user_dir, &fname_str, &chunk.dump())?;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_messages(
|
||||
storage_owner: Uuid,
|
||||
external_user: Uuid,
|
||||
loaded_messages: usize,
|
||||
amount: usize,
|
||||
) -> JsonValue {
|
||||
let user_dir = format!("users/{}/chats/{}", storage_owner, external_user);
|
||||
let path = Path::new(&user_dir);
|
||||
let mut messages = array![];
|
||||
|
||||
if !path.exists() {
|
||||
return messages;
|
||||
}
|
||||
|
||||
let mut latest_chunk_index: i32 = -1;
|
||||
if let Ok(entries) = fs::read_dir(path) {
|
||||
for entry in entries.flatten() {
|
||||
let fname = entry.file_name();
|
||||
let fname_str = fname.to_string_lossy();
|
||||
if fname_str.starts_with("msgs_") && fname_str.ends_with(".json") {
|
||||
if let Some(num) = fname_str
|
||||
.strip_prefix("msgs_")
|
||||
.and_then(|s| s.strip_suffix(".json"))
|
||||
{
|
||||
if let Ok(index) = num.parse::<i32>() {
|
||||
if index > latest_chunk_index {
|
||||
latest_chunk_index = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if latest_chunk_index == -1 {
|
||||
return messages;
|
||||
}
|
||||
|
||||
let mut to_skip = loaded_messages;
|
||||
let mut needed = amount;
|
||||
|
||||
for chunk_index in (0..=latest_chunk_index).rev() {
|
||||
if needed == 0 {
|
||||
break;
|
||||
}
|
||||
let file_name = format!("msgs_{}.json", chunk_index);
|
||||
let file_content = Self::load_file(&user_dir, &file_name);
|
||||
if file_content.is_empty() {
|
||||
continue;
|
||||
}
|
||||
if let Ok(chunk) = json::parse(&file_content) {
|
||||
for i in (0..chunk.len()).rev() {
|
||||
if needed == 0 {
|
||||
break;
|
||||
}
|
||||
if to_skip > 0 {
|
||||
to_skip -= 1;
|
||||
continue;
|
||||
}
|
||||
messages.push(chunk[i].clone()).unwrap();
|
||||
needed -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
messages
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let owner = Uuid::new_v4();
|
||||
let external = Uuid::new_v4();
|
||||
|
||||
ChatFiles::add_message(1234567890, true, owner, external, "Hello!").unwrap();
|
||||
ChatFiles::change_message_state(owner, external, 1234567890, MessageState::Read).unwrap();
|
||||
let msgs = ChatFiles::get_messages(owner, external, 0, 10);
|
||||
|
||||
println!("Messages: {}", msgs.dump());
|
||||
}
|
||||
91
src/util/chatsUtil.rs
Normal file
91
src/util/chatsUtil.rs
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
|
||||
use json::{self, Value};
|
||||
use std::fs::{self, File};
|
||||
use std::io::{Read, Write};
|
||||
use std::path::Path;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::users::Contact::Contact; // assuming you have a Contact struct in a module
|
||||
|
||||
pub struct ChatsUtil;
|
||||
|
||||
impl ChatsUtil {
|
||||
fn load_file(dir: &str, file_name: &str) -> String {
|
||||
let path = Path::new(dir).join(file_name);
|
||||
if let Ok(mut f) = File::open(&path) {
|
||||
let mut content = String::new();
|
||||
let _ = f.read_to_string(&mut content);
|
||||
content
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn save_file(dir: &str, file_name: &str, content: &str) -> std::io::Result<()> {
|
||||
fs::create_dir_all(dir)?;
|
||||
let path = Path::new(dir).join(file_name);
|
||||
let mut file = File::create(path)?;
|
||||
file.write_all(content.as_bytes())
|
||||
}
|
||||
|
||||
pub fn mod_user(storage_owner: Uuid, contact: Contact) -> std::io::Result<()> {
|
||||
let dir = format!("users/{}/contacts/", storage_owner);
|
||||
let file_name = "contacts.json";
|
||||
let s = Self::load_file(&dir, file_name);
|
||||
|
||||
let mut contacts = if !s.is_empty() {
|
||||
json::parse(&s).unwrap_or(array![])
|
||||
} else {
|
||||
array![]
|
||||
};
|
||||
|
||||
for i in 0..contacts.len() {
|
||||
if contacts[i]["userID"].as_str() == Some(&contact.user_id.to_string()) {
|
||||
contacts.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
contacts.push(contact.to_json()).unwrap();
|
||||
Self::save_file(&dir, file_name, &contacts.dump())
|
||||
}
|
||||
|
||||
pub fn get_user(storage_owner: Uuid, user_id: Uuid) -> Option<Contact> {
|
||||
let dir = format!("users/{}/contacts/", storage_owner);
|
||||
let file_name = "contacts.json";
|
||||
let s = Self::load_file(&dir, file_name);
|
||||
if s.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Ok(contacts) = json::parse(&s) {
|
||||
for i in 0..contacts.len() {
|
||||
if let Some(uid) = contacts[i]["userID"].as_str() {
|
||||
if Uuid::parse_str(uid).ok()? == user_id {
|
||||
return Contact::from_json(&contacts[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_users(storage_owner: Uuid) -> Value {
|
||||
let dir = format!("/users/{}/contacts/", storage_owner);
|
||||
let file_name = "contacts.json";
|
||||
let s = Self::load_file(&dir, file_name);
|
||||
|
||||
let mut contacts_out = array![];
|
||||
if !s.is_empty() {
|
||||
if let Ok(contacts) = json::parse(&s) {
|
||||
for i in 0..contacts.len() {
|
||||
if let Some(c) = Contact::from_json(&contacts[i]) {
|
||||
contacts_out.push(c.info()).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contacts_out
|
||||
}
|
||||
}
|
||||
65
src/util/configUtil.rs
Normal file
65
src/util/configUtil.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
use json::JsonValue;
|
||||
use std::fs::{self, File};
|
||||
use std::io::{Read, Write};
|
||||
use std::path::Path;
|
||||
|
||||
use crate::files::Files;
|
||||
|
||||
pub struct ConfigUtil {
|
||||
pub config: JsonValue,
|
||||
pub unique: bool,
|
||||
}
|
||||
|
||||
impl ConfigUtil {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
config: JsonValue::new_object(),
|
||||
unique: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn load_file(path: &str) -> String {
|
||||
if let Ok(mut f) = File::open(path) {
|
||||
let mut content = String::new();
|
||||
let _ = f.read_to_string(&mut content);
|
||||
content
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn save_file(path: &str, content: &str) -> std::io::Result<()> {
|
||||
if let Some(parent) = Path::new(path).parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
let mut file = File::create(path)?;
|
||||
file.write_all(content.as_bytes())
|
||||
}
|
||||
|
||||
pub fn load(&mut self) {
|
||||
let s = Self::load_file(Files::MAIN);
|
||||
if !s.is_empty() {
|
||||
self.config = json::parse(&s).unwrap_or(JsonValue::new_object());
|
||||
}
|
||||
if self.config.has("port") {
|
||||
if let Some(port) = self.config["port"].as_i32() {
|
||||
// Set community manager port
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change(&mut self, key: &str, value: JsonValue) {
|
||||
self.config[key] = value;
|
||||
self.unique = true;
|
||||
}
|
||||
|
||||
pub fn update(&mut self) {
|
||||
if self.unique {
|
||||
let _ = self.save();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save(&self) -> std::io::Result<()> {
|
||||
Self::save_file(Files::MAIN, &self.config.dump())
|
||||
}
|
||||
}
|
||||
162
src/util/fileUtil.rs
Normal file
162
src/util/fileUtil.rs
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
use std::fs::{self, File};
|
||||
use std::io::{Read, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::SystemTime;
|
||||
use std::ffi::OsStr;
|
||||
use std::process;
|
||||
use walkdir::WalkDir;
|
||||
use std::fmt::Write as FmtWrite;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub struct FileUtil;
|
||||
|
||||
impl FileUtil {
|
||||
pub fn delete_file(path: &str, name: &str) -> bool {
|
||||
let dir = Path::new(&Self::get_jar_directory()).join(path);
|
||||
let file = dir.join(name);
|
||||
if !file.exists() {
|
||||
return false;
|
||||
}
|
||||
fs::remove_file(file).is_ok()
|
||||
}
|
||||
|
||||
pub fn delete_directory(path: &str) -> bool {
|
||||
let dir = Path::new(&Self::get_jar_directory()).join(path);
|
||||
Self::delete_dir_recursive(&dir)
|
||||
}
|
||||
|
||||
fn delete_dir_recursive(directory: &Path) -> bool {
|
||||
if !directory.exists() {
|
||||
return false;
|
||||
}
|
||||
if let Err(e) = fs::remove_dir_all(directory) {
|
||||
println!("[IMPORTANT] Couldn't delete directory {}: {}", directory.display(), e);
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn delete_user_directory(user_id: Uuid) {
|
||||
let user_dir = Path::new(&Self::get_jar_directory())
|
||||
.join("users")
|
||||
.join(user_id.to_string());
|
||||
let _ = Self::delete_dir_recursive(&user_dir);
|
||||
}
|
||||
|
||||
pub fn load_file(path: &str, name: &str) -> String {
|
||||
let dir = Path::new(&Self::get_jar_directory()).join(path);
|
||||
let file_path = dir.join(name);
|
||||
|
||||
if !dir.exists() {
|
||||
if let Err(e) = fs::create_dir_all(&dir) {
|
||||
println!("[IMPORTANT] Couldn't create directories: {}", e);
|
||||
return String::new();
|
||||
}
|
||||
return String::new();
|
||||
}
|
||||
|
||||
if !file_path.exists() {
|
||||
if let Err(e) = File::create(&file_path) {
|
||||
println!("[IMPORTANT] Couldn't create file: {}", e);
|
||||
}
|
||||
return String::new();
|
||||
}
|
||||
|
||||
let mut content = String::new();
|
||||
if let Ok(mut f) = File::open(&file_path) {
|
||||
let _ = f.read_to_string(&mut content);
|
||||
}
|
||||
content
|
||||
}
|
||||
|
||||
pub fn save_file(path: &str, name: &str, value: &str) {
|
||||
let dir = Path::new(&Self::get_jar_directory()).join(path);
|
||||
let file_path = dir.join(name);
|
||||
|
||||
if !dir.exists() {
|
||||
if let Err(e) = fs::create_dir_all(&dir) {
|
||||
println!("[IMPORTANT] Couldn't create directories: {}", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = fs::write(&file_path, value) {
|
||||
println!("[IMPORTANT] Couldn't write file {}: {}", file_path.display(), e);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_jar_directory() -> String {
|
||||
// In Rust, use current_exe as a proxy for JAR directory
|
||||
let exe = std::env::current_exe().unwrap_or_else(|_| PathBuf::from("."));
|
||||
exe.parent()
|
||||
.unwrap_or(Path::new("."))
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
pub fn used_space() -> u64 {
|
||||
Self::get_directory_size(&PathBuf::from(Self::get_jar_directory()))
|
||||
}
|
||||
|
||||
pub fn get_directory_size(directory: &Path) -> u64 {
|
||||
let mut size = 0;
|
||||
for entry in WalkDir::new(directory).into_iter().filter_map(|e| e.ok()) {
|
||||
let path = entry.path();
|
||||
if path.is_file() {
|
||||
if let Ok(metadata) = path.metadata() {
|
||||
size += path
|
||||
.file_name()
|
||||
.unwrap_or(OsStr::new(""))
|
||||
.len() as u64;
|
||||
size += metadata.len();
|
||||
}
|
||||
}
|
||||
}
|
||||
size
|
||||
}
|
||||
|
||||
pub fn get_designed_storage(user_id: Uuid) -> String {
|
||||
let user_dir = Path::new(&Self::get_jar_directory())
|
||||
.join("users")
|
||||
.join(user_id.to_string());
|
||||
Self::design_byte(Self::get_directory_size(&user_dir))
|
||||
}
|
||||
|
||||
pub fn design_byte(bytes: u64) -> String {
|
||||
let mut hr_size = format!("{:.2}B", bytes as f64);
|
||||
let k = bytes as f64 / 1024.0;
|
||||
let m = k / 1024.0;
|
||||
let g = m / 1024.0;
|
||||
let t = g / 1024.0;
|
||||
|
||||
if t >= 1.0 {
|
||||
hr_size = format!("{:.2}TB", t);
|
||||
} else if g >= 1.0 {
|
||||
hr_size = format!("{:.2}GB", g);
|
||||
} else if m >= 1.0 {
|
||||
hr_size = format!("{:.2}MB", m);
|
||||
} else if k >= 1.0 {
|
||||
hr_size = format!("{:.2}KB", k);
|
||||
}
|
||||
hr_size
|
||||
}
|
||||
|
||||
pub fn get_used_ram() -> String {
|
||||
// Rust has no direct Runtime like Java.
|
||||
// As a placeholder, read process memory usage from sysinfo.
|
||||
use sysinfo::{System, SystemExt, ProcessExt};
|
||||
|
||||
let mut sys = System::new_all();
|
||||
sys.refresh_all();
|
||||
if let Some(process) = sys.process(process::id() as i32) {
|
||||
let used = process.memory() * 1024; // kB to bytes
|
||||
let total = sys.total_memory() * 1024;
|
||||
return format!(
|
||||
"{}/{}",
|
||||
Self::design_byte(used),
|
||||
Self::design_byte(total)
|
||||
);
|
||||
}
|
||||
"Unknown".to_string()
|
||||
}
|
||||
}
|
||||
4
src/util/mod.rs
Normal file
4
src/util/mod.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
pub mod fileUtil;
|
||||
pub mod chatFiles;
|
||||
pub mod configUtil;
|
||||
pub mod chatsUtil;
|
||||
Loading…
Reference in a new issue