cli_text_reader_online/
lib.rs1mod config;
2mod editor;
3mod progress;
4mod server;
5mod tutorial;
6
7use chrono::Utc;
8use editor::Editor;
9use server::{HyggClient, ReadingProgress};
10use std::collections::hash_map::DefaultHasher;
11use std::hash::{Hash, Hasher};
12use std::path::Path;
13use tokio::fs::read_to_string;
14use uuid::Uuid;
15
16pub async fn upload_file_to_server(
30 local_file_path: String,
31 user_id: String,
32) -> Result<(), Box<dyn std::error::Error>> {
33 if !Path::new(&local_file_path).exists() {
35 return Err(format!("File not found: {local_file_path}").into());
36 }
37
38 let content = read_to_string(&local_file_path).await?.replace('\r', ""); let file_name = Path::new(&local_file_path)
43 .file_name()
44 .and_then(|name| name.to_str())
45 .ok_or_else(|| format!("Invalid file path: {local_file_path}"))?;
46
47 let client = HyggClient::new(user_id.clone());
49 client.upload_file(file_name, &content).await?;
50
51 println!("Successfully uploaded {file_name} to the server");
52 Ok(())
53}
54
55pub async fn run_cli_text_reader(
56 file_path: String,
57 user_id: String,
58 col: usize,
59) -> Result<(), Box<dyn std::error::Error>> {
60 let client = HyggClient::new(user_id.clone());
61
62 let content = client.get_file_content(&file_path).await?;
64 let lines: Vec<String> = content.lines().map(String::from).collect();
65
66 let mut hasher = DefaultHasher::new();
69 file_path.hash(&mut hasher);
70 let hash = hasher.finish();
71
72 let hash_bytes = hash.to_be_bytes();
75 let uuid_bytes = [
76 hash_bytes[0],
77 hash_bytes[1],
78 hash_bytes[2],
79 hash_bytes[3],
80 hash_bytes[4],
81 hash_bytes[5],
82 hash_bytes[6],
83 hash_bytes[7],
84 hash_bytes[0],
85 hash_bytes[1],
86 hash_bytes[2],
87 hash_bytes[3],
88 hash_bytes[4],
89 hash_bytes[5],
90 hash_bytes[6],
91 hash_bytes[7],
92 ];
93
94 let file_uuid = Uuid::from_bytes(uuid_bytes);
95 println!("Using file-based UUID: {file_uuid}");
96
97 let mut progress = ReadingProgress {
98 id: file_uuid,
99 file_path,
100 position: 0,
101 user_id: user_id.clone(),
102 last_accessed: Utc::now(),
103 lock_holder: None,
104 lock_expiry: None,
105 };
106
107 let read_only_mode = match client.acquire_lock(&progress).await {
109 Ok(locked_progress) => {
110 progress = locked_progress;
111 false }
113 Err(e) => {
114 if e.to_string().contains("locked by") {
116 println!(
117 "\nOpening in READ-ONLY mode because file is locked by another user."
118 );
119 println!("Close and reopen to attempt to acquire the lock.\n");
120
121 match client.get_progress(&progress.id.to_string()).await {
123 Ok(current_progress) => {
124 progress = current_progress;
125 true }
127 Err(_) => {
128 true }
131 }
132 } else {
133 return Err(Box::new(std::io::Error::other(e.to_string())));
135 }
136 }
137 };
138
139 let mut editor = Editor::new(lines, col);
141 editor.set_position(progress.position);
142
143 let result = if read_only_mode {
144 editor.set_read_only(true);
145 editor.run()
147 } else {
148 let client_clone = client.clone();
150 let progress_clone = progress.clone();
151
152 editor.run_with_progress(move |pos| {
153 let mut progress_update = progress_clone.clone();
154 progress_update.position = pos;
155 let client = client_clone.clone();
156 tokio::spawn(async move {
157 if let Err(e) = client.update_progress(&progress_update).await {
158 eprintln!("Failed to update progress: {e}");
159 }
160 });
161 })
162 };
163
164 if !read_only_mode {
166 match client.release_lock(&progress).await {
167 Ok(_) => println!(
168 "\nLock released successfully. Other users can now edit this file."
169 ),
170 Err(e) => eprintln!("\nFailed to release lock: {e}"),
171 }
172 }
173
174 result
175}