use sqlx::PgPool;
use uuid::Uuid;
use crate::db::repositories::documents as repo;
use crate::domain::documents::{Document, DocumentSummary};
use crate::error::{AppError, AppResult};
const DEFAULT_LIST_LIMIT: i64 = 20;
pub const DOCUMENT_STATUSES: [&str; 3] = ["new", "extracted", "archived"];
pub async fn create_document(
pool: &PgPool,
title: &str,
source_type: &str,
raw_text: &str,
) -> AppResult<Document> {
let title = title.trim();
let source_type = source_type.trim();
if title.is_empty() {
return Err(AppError::Validation("title must not be empty".into()));
}
if source_type.is_empty() {
return Err(AppError::Validation("source_type must not be empty".into()));
}
if raw_text.trim().is_empty() {
return Err(AppError::Validation("raw_text must not be empty".into()));
}
Ok(repo::create(pool, title, source_type, raw_text).await?)
}
pub async fn get_document(pool: &PgPool, id: Uuid) -> AppResult<Document> {
repo::get(pool, id)
.await?
.ok_or_else(|| AppError::NotFound(format!("document {id} not found")))
}
pub async fn list_recent_documents(
pool: &PgPool,
status: Option<&str>,
limit: Option<i64>,
) -> AppResult<Vec<DocumentSummary>> {
if let Some(s) = status {
validate_status(s)?;
}
let limit = normalize_limit(limit);
Ok(repo::list_recent(pool, status, limit).await?)
}
pub async fn set_document_status(
pool: &PgPool,
id: Uuid,
status: &str,
) -> AppResult<Document> {
validate_status(status)?;
repo::update_status(pool, id, status)
.await?
.ok_or_else(|| AppError::NotFound(format!("document {id} not found")))
}
pub async fn mark_extracted_if_new(pool: &PgPool, id: Uuid) -> AppResult<()> {
let doc = repo::get(pool, id).await?;
if let Some(doc) = doc {
if doc.status == "new" {
repo::update_status(pool, id, "extracted").await?;
}
}
Ok(())
}
fn validate_status(status: &str) -> AppResult<()> {
if DOCUMENT_STATUSES.contains(&status) {
Ok(())
} else {
Err(AppError::Validation(format!(
"invalid document status `{status}`; expected one of: {}",
DOCUMENT_STATUSES.join(", ")
)))
}
}
pub fn normalize_limit(limit: Option<i64>) -> i64 {
limit.unwrap_or(DEFAULT_LIST_LIMIT).clamp(1, 200)
}