81 lines
3.0 KiB
Rust
81 lines
3.0 KiB
Rust
//! Storage service (Phase 2, FR-STORE-*, FR-REL-*). SQLite index + on-disk
|
|
//! audio/transcript/notes files (ADR-0006). Async via sqlx.
|
|
//!
|
|
//! Invariant: audio is the source of truth. Derived artifacts (transcript/notes/
|
|
//! summary) are regenerable; retention never touches an in-progress meeting.
|
|
|
|
use crate::models::{MeetingId, MeetingListItem};
|
|
use async_trait::async_trait;
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
pub enum StoreError {
|
|
#[error("db error: {0}")]
|
|
Db(String),
|
|
#[error("io error: {0}")]
|
|
Io(#[from] std::io::Error),
|
|
#[error("not found: {0}")]
|
|
NotFound(String),
|
|
}
|
|
|
|
pub struct NewMeeting {
|
|
pub title: String,
|
|
pub calendar_event_id: Option<String>,
|
|
}
|
|
|
|
pub struct Retention {
|
|
pub max_age_days: Option<u32>,
|
|
pub max_size_gb: Option<u32>,
|
|
}
|
|
|
|
#[async_trait]
|
|
pub trait Store: Send + Sync {
|
|
async fn create_meeting(&self, m: NewMeeting) -> Result<MeetingId, StoreError>;
|
|
async fn finalize_meeting(&self, id: &MeetingId) -> Result<(), StoreError>;
|
|
async fn list_meetings(
|
|
&self,
|
|
query: Option<String>,
|
|
) -> Result<Vec<MeetingListItem>, StoreError>;
|
|
async fn delete_meeting(&self, id: &MeetingId) -> Result<(), StoreError>;
|
|
async fn update_notes(&self, id: &MeetingId, markdown: &str) -> Result<(), StoreError>;
|
|
/// Full-text search across transcripts + notes (Phase 8, FR-SEARCH-1).
|
|
async fn search(&self, query: &str) -> Result<Vec<MeetingListItem>, StoreError>;
|
|
/// Startup reconcile: meetings with audio but no finalized transcript (FR-REL-1).
|
|
async fn recover_scan(&self) -> Result<Vec<MeetingId>, StoreError>;
|
|
/// Enforce retention; returns count removed. Skips in-progress meetings.
|
|
async fn enforce_retention(&self, policy: Retention) -> Result<u32, StoreError>;
|
|
}
|
|
|
|
/// SQLite-backed store. Migrations live in `migrations/` (sqlx::migrate!).
|
|
pub struct SqliteStore;
|
|
|
|
#[async_trait]
|
|
impl Store for SqliteStore {
|
|
async fn create_meeting(&self, _m: NewMeeting) -> Result<MeetingId, StoreError> {
|
|
todo!("Phase 2 — create meeting row + folder")
|
|
}
|
|
async fn finalize_meeting(&self, _id: &MeetingId) -> Result<(), StoreError> {
|
|
todo!("Phase 2 — persist transcript.json + metadata")
|
|
}
|
|
async fn list_meetings(
|
|
&self,
|
|
_query: Option<String>,
|
|
) -> Result<Vec<MeetingListItem>, StoreError> {
|
|
todo!("Phase 2 — list meetings")
|
|
}
|
|
async fn delete_meeting(&self, _id: &MeetingId) -> Result<(), StoreError> {
|
|
todo!("Phase 2 — delete meeting + folder")
|
|
}
|
|
async fn update_notes(&self, _id: &MeetingId, _markdown: &str) -> Result<(), StoreError> {
|
|
todo!("Phase 2 — write notes.md + FTS")
|
|
}
|
|
async fn search(&self, _query: &str) -> Result<Vec<MeetingListItem>, StoreError> {
|
|
todo!("Phase 8 — FTS5 search")
|
|
}
|
|
async fn recover_scan(&self) -> Result<Vec<MeetingId>, StoreError> {
|
|
todo!("Phase 2 — recovery scan")
|
|
}
|
|
async fn enforce_retention(&self, _policy: Retention) -> Result<u32, StoreError> {
|
|
todo!("Phase 2 — retention enforcement")
|
|
}
|
|
}
|