Skip to main content

vibe_graph_ops/
requests.rs

1//! Request DTOs for operations.
2//!
3//! Each request type encapsulates all the parameters needed for an operation,
4//! making it easy to call from CLI, REST API, or programmatically.
5
6use std::path::PathBuf;
7
8use serde::{Deserialize, Serialize};
9
10use crate::workspace::SyncSource;
11
12/// Request to sync a codebase (local or remote).
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct SyncRequest {
15    /// The source to sync from.
16    pub source: SyncSource,
17
18    /// Repositories to ignore when syncing an org.
19    #[serde(default)]
20    pub ignore: Vec<String>,
21
22    /// Whether to skip saving to .self folder.
23    #[serde(default)]
24    pub no_save: bool,
25
26    /// Whether to create a timestamped snapshot.
27    #[serde(default)]
28    pub snapshot: bool,
29
30    /// Clone to global cache directory instead of current directory.
31    #[serde(default)]
32    pub use_cache: bool,
33
34    /// Whether to force a fresh sync even if .self exists.
35    #[serde(default)]
36    pub force: bool,
37}
38
39impl SyncRequest {
40    /// Create a sync request for a local path.
41    pub fn local(path: impl Into<PathBuf>) -> Self {
42        Self {
43            source: SyncSource::local(path),
44            ignore: vec![],
45            no_save: false,
46            snapshot: false,
47            use_cache: false,
48            force: false,
49        }
50    }
51
52    /// Create a sync request for a GitHub organization.
53    pub fn github_org(org: impl Into<String>) -> Self {
54        Self {
55            source: SyncSource::github_org(org),
56            ignore: vec![],
57            no_save: false,
58            snapshot: false,
59            use_cache: false,
60            force: false,
61        }
62    }
63
64    /// Create a sync request for a GitHub repository.
65    pub fn github_repo(owner: impl Into<String>, repo: impl Into<String>) -> Self {
66        Self {
67            source: SyncSource::github_repo(owner, repo),
68            ignore: vec![],
69            no_save: false,
70            snapshot: false,
71            use_cache: false,
72            force: false,
73        }
74    }
75
76    /// Parse and detect the source type from a string.
77    pub fn detect(input: &str) -> Self {
78        Self {
79            source: SyncSource::detect(input),
80            ignore: vec![],
81            no_save: false,
82            snapshot: false,
83            use_cache: false,
84            force: false,
85        }
86    }
87
88    /// Add repositories to ignore.
89    pub fn with_ignore(mut self, ignore: impl IntoIterator<Item = impl Into<String>>) -> Self {
90        self.ignore.extend(ignore.into_iter().map(|s| s.into()));
91        self
92    }
93
94    /// Skip saving to .self folder.
95    pub fn without_save(mut self) -> Self {
96        self.no_save = true;
97        self
98    }
99
100    /// Create a snapshot after sync.
101    pub fn with_snapshot(mut self) -> Self {
102        self.snapshot = true;
103        self
104    }
105
106    /// Use global cache for cloning.
107    pub fn use_cache(mut self) -> Self {
108        self.use_cache = true;
109        self
110    }
111
112    /// Force a fresh sync.
113    pub fn force(mut self) -> Self {
114        self.force = true;
115        self
116    }
117}
118
119/// Request to build a source code graph.
120#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct GraphRequest {
122    /// Path to the workspace.
123    pub path: PathBuf,
124
125    /// Output path for the graph JSON (optional).
126    #[serde(skip_serializing_if = "Option::is_none")]
127    pub output: Option<PathBuf>,
128
129    /// Whether to rebuild even if cached graph exists.
130    #[serde(default)]
131    pub force: bool,
132}
133
134impl GraphRequest {
135    /// Create a graph request for a path.
136    pub fn new(path: impl Into<PathBuf>) -> Self {
137        Self {
138            path: path.into(),
139            output: None,
140            force: false,
141        }
142    }
143
144    /// Set the output path.
145    pub fn with_output(mut self, output: impl Into<PathBuf>) -> Self {
146        self.output = Some(output.into());
147        self
148    }
149
150    /// Force rebuild.
151    pub fn force(mut self) -> Self {
152        self.force = true;
153        self
154    }
155}
156
157/// Request to get workspace status.
158#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct StatusRequest {
160    /// Path to check.
161    pub path: PathBuf,
162
163    /// Include detailed repository info.
164    #[serde(default)]
165    pub detailed: bool,
166}
167
168impl StatusRequest {
169    /// Create a status request for a path.
170    pub fn new(path: impl Into<PathBuf>) -> Self {
171        Self {
172            path: path.into(),
173            detailed: false,
174        }
175    }
176
177    /// Include detailed info.
178    pub fn detailed(mut self) -> Self {
179        self.detailed = true;
180        self
181    }
182}
183
184/// Request to load a project from .self store.
185#[derive(Debug, Clone, Serialize, Deserialize)]
186pub struct LoadRequest {
187    /// Path to the workspace.
188    pub path: PathBuf,
189}
190
191impl LoadRequest {
192    /// Create a load request for a path.
193    pub fn new(path: impl Into<PathBuf>) -> Self {
194        Self { path: path.into() }
195    }
196}
197
198/// Request to compose output from a project.
199#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct ComposeRequest {
201    /// Path to the workspace.
202    pub path: PathBuf,
203
204    /// Output format.
205    #[serde(default)]
206    pub format: ComposeFormat,
207
208    /// Output path (optional, defaults to stdout or <name>.<ext>).
209    #[serde(skip_serializing_if = "Option::is_none")]
210    pub output: Option<PathBuf>,
211
212    /// Force resync even if .self exists.
213    #[serde(default)]
214    pub force: bool,
215}
216
217/// Output format for compose.
218#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
219#[serde(rename_all = "lowercase")]
220pub enum ComposeFormat {
221    /// Markdown format.
222    #[default]
223    Markdown,
224    /// JSON format.
225    Json,
226}
227
228impl std::str::FromStr for ComposeFormat {
229    type Err = String;
230
231    fn from_str(s: &str) -> Result<Self, Self::Err> {
232        match s.to_lowercase().as_str() {
233            "md" | "markdown" => Ok(Self::Markdown),
234            "json" => Ok(Self::Json),
235            _ => Err(format!("Unknown format: {}", s)),
236        }
237    }
238}
239
240impl ComposeRequest {
241    /// Create a compose request for a path.
242    pub fn new(path: impl Into<PathBuf>) -> Self {
243        Self {
244            path: path.into(),
245            format: ComposeFormat::Markdown,
246            output: None,
247            force: false,
248        }
249    }
250
251    /// Set the output format.
252    pub fn format(mut self, format: ComposeFormat) -> Self {
253        self.format = format;
254        self
255    }
256
257    /// Set the output path.
258    pub fn with_output(mut self, output: impl Into<PathBuf>) -> Self {
259        self.output = Some(output.into());
260        self
261    }
262
263    /// Force resync.
264    pub fn force(mut self) -> Self {
265        self.force = true;
266        self
267    }
268}
269
270/// Request to start the serve command.
271#[derive(Debug, Clone, Serialize, Deserialize)]
272pub struct ServeRequest {
273    /// Path to the workspace.
274    pub path: PathBuf,
275
276    /// Port to serve on.
277    #[serde(default = "default_port")]
278    pub port: u16,
279
280    /// Path to WASM build artifacts.
281    #[serde(skip_serializing_if = "Option::is_none")]
282    pub wasm_dir: Option<PathBuf>,
283}
284
285fn default_port() -> u16 {
286    3000
287}
288
289impl ServeRequest {
290    /// Create a serve request for a path.
291    pub fn new(path: impl Into<PathBuf>) -> Self {
292        Self {
293            path: path.into(),
294            port: default_port(),
295            wasm_dir: None,
296        }
297    }
298
299    /// Set the port.
300    pub fn port(mut self, port: u16) -> Self {
301        self.port = port;
302        self
303    }
304
305    /// Set the WASM directory.
306    pub fn wasm_dir(mut self, dir: impl Into<PathBuf>) -> Self {
307        self.wasm_dir = Some(dir.into());
308        self
309    }
310}
311
312/// Request to clean the .self folder.
313#[derive(Debug, Clone, Serialize, Deserialize)]
314pub struct CleanRequest {
315    /// Path to the workspace.
316    pub path: PathBuf,
317}
318
319impl CleanRequest {
320    /// Create a clean request for a path.
321    pub fn new(path: impl Into<PathBuf>) -> Self {
322        Self { path: path.into() }
323    }
324}
325
326/// Request to get git changes for a workspace.
327#[derive(Debug, Clone, Serialize, Deserialize)]
328pub struct GitChangesRequest {
329    /// Path to the workspace.
330    pub path: PathBuf,
331}
332
333impl GitChangesRequest {
334    /// Create a git changes request for a path.
335    pub fn new(path: impl Into<PathBuf>) -> Self {
336        Self { path: path.into() }
337    }
338}