Add all scaffolding, design, and decision documentation AKA first commit
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
//! 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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user