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}