use anyhow::Result;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use uuid::Uuid;
const SERVER_URL: &str = "http://localhost:3001";
#[derive(Debug, Error)]
pub enum ClientError {
#[error("HTTP error: {0}")]
Http(#[from] reqwest::Error),
#[error("Lock error: {0}")]
Lock(String),
#[error("Upload error: {0}")]
Upload(String),
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ReadingProgress {
pub id: Uuid,
pub file_path: String,
pub position: usize,
pub user_id: String,
pub last_accessed: DateTime<Utc>,
pub lock_holder: Option<String>,
pub lock_expiry: Option<DateTime<Utc>>,
}
#[derive(Clone)]
pub struct HyggClient {
client: reqwest::Client,
user_id: String,
}
impl HyggClient {
pub fn new(user_id: String) -> Self {
Self { client: reqwest::Client::new(), user_id }
}
pub async fn get_file_content(&self, file_path: &str) -> Result<String> {
let file_name = std::path::Path::new(file_path)
.file_name()
.and_then(|name| name.to_str())
.unwrap_or("sample.txt");
let url = format!("{SERVER_URL}/file/{file_name}");
println!("Requesting file from: {url}");
let content =
self.client.get(&url).send().await?.error_for_status()?.text().await?;
Ok(content)
}
pub async fn acquire_lock(
&self,
progress: &ReadingProgress,
) -> Result<ReadingProgress> {
let url = format!("{SERVER_URL}/progress/lock");
let response = self.client.post(&url).json(progress).send().await?;
if !response.status().is_success() {
let error = response.text().await?;
return Err(ClientError::Lock(error).into());
}
let progress = response.json().await?;
Ok(progress)
}
pub async fn update_progress(
&self,
progress: &ReadingProgress,
) -> Result<ReadingProgress> {
let url = format!("{SERVER_URL}/progress/update");
let response = self.client.post(&url).json(progress).send().await?;
if !response.status().is_success() {
let error = response.text().await?;
return Err(ClientError::Lock(error).into());
}
let progress = response.json().await?;
Ok(progress)
}
pub async fn get_progress(
&self,
progress_id: &str,
) -> Result<ReadingProgress> {
let url = format!("{SERVER_URL}/progress/{progress_id}");
let response = self.client.get(&url).send().await?;
if !response.status().is_success() {
let error = response.text().await?;
return Err(
ClientError::Lock(format!("Failed to get progress: {error}")).into(),
);
}
let progress = response.json().await?;
Ok(progress)
}
pub async fn release_lock(
&self,
progress: &ReadingProgress,
) -> Result<ReadingProgress> {
let url = format!("{SERVER_URL}/progress/release");
let response = self.client.post(&url).json(progress).send().await?;
if !response.status().is_success() {
let error = response.text().await?;
return Err(
ClientError::Lock(format!("Failed to release lock: {error}")).into(),
);
}
let progress = response.json().await?;
Ok(progress)
}
pub async fn upload_file(
&self,
file_path: &str,
content: &str,
) -> Result<()> {
let url = format!("{SERVER_URL}/file/upload");
#[derive(Serialize)]
struct FileUpload {
file_path: String,
content: String,
}
let upload = FileUpload {
file_path: file_path.to_string(),
content: content.to_string(),
};
let response = self.client.post(&url).json(&upload).send().await?;
if !response.status().is_success() {
let error = response.text().await?;
return Err(
ClientError::Upload(format!("Failed to upload file: {error}")).into(),
);
}
Ok(())
}
}