Skip to main content

vdsl_sync/application/
sdk.rs

1//! SyncStore SDK — vdsl-syncの外部向けAPI面。
2//!
3//! MCP・Lua Bridge等のインターフェース層はこのtraitのみに依存する。
4//! scan→delta→plan→execute等の内部パイプラインは一切漏れない。
5//!
6//! # 設計方針
7//!
8//! - **UseCase完結**: `sync()`一発で全工程が内部完結
9//! - **CRUD + Query**: Firestore的なput/get/list/delete + status/errors/pending
10//! - **進捗**: `status()` でDB SELECTベースのサマリーを取得(Observer不要)
11//! - **型安全**: 戻り型は全てSDK専用型。旧Store/SyncFacadeの型に依存しない
12
13use std::path::Path;
14
15use async_trait::async_trait;
16
17use super::topology_store::TopologyFileView;
18use crate::application::error::SyncError;
19use crate::domain::file_type::FileType;
20use crate::domain::fingerprint::FileFingerprint;
21use crate::domain::location::{LocationId, SyncSummary};
22use crate::domain::view::{ErrorEntry, PendingEntry};
23use crate::infra::backend::ProgressFn;
24
25// =============================================================================
26// SDK Result types
27// =============================================================================
28
29/// sync/sync_route の結果。
30///
31/// 旧BatchResult/SyncResult/FacadeSyncResultを統合した単一型。
32/// Serialize対応でMCP層がそのまま返せる。
33#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
34pub struct SyncReport {
35    /// スキャンで検出されたファイル数。
36    pub scanned: usize,
37    /// スキャン時の非致命的エラー(個別ファイル読み取り失敗等)。
38    pub scan_errors: Vec<SyncReportError>,
39    /// 計画で作成されたTransfer数。
40    pub transfers_created: usize,
41    /// 実行で成功した転送数。
42    pub transferred: usize,
43    /// 実行で失敗した転送数。
44    pub failed: usize,
45    /// 転送失敗詳細。
46    pub errors: Vec<SyncReportError>,
47    /// 検出されたコンフリクト。
48    ///
49    /// 複数Locationで同一ファイルが異なる内容に更新された場合に報告される。
50    /// コンフリクトのあるファイルのUpdate転送は実行されない。
51    #[serde(skip_serializing_if = "Vec::is_empty")]
52    pub conflicts: Vec<SyncReportConflict>,
53}
54
55/// コンフリクト報告。SDK面の型。
56///
57/// domain::distribute::ConflictEntry から変換して使用する。
58#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
59pub struct SyncReportConflict {
60    pub file_id: String,
61    pub path: String,
62    /// コンフリクトしているLocation群。
63    pub locations: Vec<String>,
64}
65
66impl From<&crate::domain::distribute::ConflictEntry> for SyncReportConflict {
67    fn from(c: &crate::domain::distribute::ConflictEntry) -> Self {
68        Self {
69            file_id: c.topology_file_id().to_string(),
70            path: c.relative_path().to_string(),
71            locations: c
72                .variants()
73                .iter()
74                .map(|v| v.location_id().to_string())
75                .collect(),
76        }
77    }
78}
79
80/// 転送失敗の詳細。
81#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
82pub struct SyncReportError {
83    pub path: String,
84    pub error: String,
85}
86
87/// put() の結果。
88///
89/// TopologyPutResultをre-exportせず、SDK面として安定させる。
90#[derive(Debug, serde::Serialize)]
91pub struct PutReport {
92    /// 登録/更新されたファイルID。
93    pub file_id: String,
94    /// 新規登録 = true、更新 = false。
95    pub is_new: bool,
96    /// 作成されたTransfer数。
97    pub transfers_created: usize,
98}
99
100// =============================================================================
101// SDK Trait
102// =============================================================================
103
104/// vdsl-syncの外部向けAPI。
105///
106/// インターフェース層(MCP, Lua Bridge, CLI)はこのtraitのみに依存する。
107/// 内部実装(TopologyStore, TransferEngine, scan, delta等)は一切公開しない。
108#[async_trait]
109pub trait SyncStoreSdk: Send + Sync {
110    // =========================================================================
111    // UseCase — 同期操作
112    // =========================================================================
113
114    /// 全体同期: scan→delta→plan→execute 一括。
115    async fn sync(&self) -> Result<SyncReport, SyncError>;
116
117    /// 単一ルート同期。
118    async fn sync_route(
119        &self,
120        src: &LocationId,
121        dest: &LocationId,
122    ) -> Result<SyncReport, SyncError>;
123
124    // =========================================================================
125    // Command — ファイル操作
126    // =========================================================================
127
128    /// ファイル登録。
129    async fn put(
130        &self,
131        path: &str,
132        file_type: FileType,
133        fingerprint: FileFingerprint,
134        origin: &LocationId,
135        embedded_id: Option<String>,
136    ) -> Result<PutReport, SyncError>;
137
138    /// ファイル削除。削除されたTransfer数を返す。
139    async fn delete(&self, path: &str) -> Result<usize, SyncError>;
140
141    /// アーカイブからファイル復元。
142    ///
143    /// `delete()` で archive 化されたファイル(cloud上の `vdsl/archived/{revision}/{path}`)を
144    /// 元の場所(`vdsl/output/{path}`)に moveto reverse で戻し、TopologyFile の
145    /// deleted フラグを解除する。直後の `sync()` の cloud scan により LocationFile が再登録され、
146    /// distribute → 他拠点へ再配布される。
147    async fn restore(&self, path: &str, revision: &str) -> Result<(), SyncError>;
148
149    // =========================================================================
150    // Query — 読み取り
151    // =========================================================================
152
153    /// ファイル取得。
154    async fn get(&self, path: &str) -> Result<Option<TopologyFileView>, SyncError>;
155
156    /// ファイル一覧。
157    async fn list(
158        &self,
159        file_type: Option<FileType>,
160        limit: Option<usize>,
161    ) -> Result<Vec<TopologyFileView>, SyncError>;
162
163    /// ロケーション別同期サマリー。
164    async fn status(&self) -> Result<SyncSummary, SyncError>;
165
166    /// エラー一覧。
167    async fn errors(&self) -> Result<Vec<ErrorEntry>, SyncError>;
168
169    /// 転送待ち一覧。
170    async fn pending(&self, dest: &LocationId) -> Result<Vec<PendingEntry>, SyncError>;
171
172    // =========================================================================
173    // Topology — 読み取り専用
174    // =========================================================================
175
176    /// 登録済みロケーション一覧。
177    fn locations(&self) -> Vec<LocationId>;
178
179    /// 全エッジ `(src, dest)` 一覧。
180    fn all_edges(&self) -> Vec<(LocationId, LocationId)>;
181
182    /// ローカルファイルルート。
183    fn local_root(&self) -> Option<&Path>;
184
185    /// Set a progress callback for reporting phase/chunk progress.
186    ///
187    /// Called by `SyncTaskManager` before spawning sync.
188    /// The callback receives human-readable messages like:
189    /// - `"phase: scan (1200 files)"`
190    /// - `"push: chunk 5/22 (500/2111)"`
191    ///
192    /// Default: no-op.
193    fn set_progress_callback(&self, _callback: Option<ProgressFn>) {}
194}