ryo_app/api/types.rs
1//! Request/Response types for the Api.
2//!
3//! These types are used both for direct API calls and for RPC transport (tarpc).
4
5use crate::intent::Goal;
6use ryo_analysis::SymbolKind;
7use ryo_suggest::EnhancedSuggestion;
8use serde::{Deserialize, Serialize};
9use std::path::PathBuf;
10
11// Re-export from ryo-query-language for unified types
12pub use ryo_query_language::{QueryResponse, ViewMode};
13
14// ============================================================================
15// Execution Status (HTTP-inspired status codes for CLI results)
16// ============================================================================
17
18/// Status code for execution results, inspired by HTTP status codes.
19///
20/// Provides a systematic categorization of execution outcomes:
21/// - 2xx: Success (operation completed as intended)
22/// - 4xx: Client Error (invalid input, not found, conflicts)
23///
24/// # Example
25///
26/// ```ignore
27/// match status.code {
28/// StatusCode::Ok => println!("Changes applied successfully"),
29/// StatusCode::NoChange => println!("No changes needed"),
30/// StatusCode::Conflict => println!("Conflicts require resolution"),
31/// _ => {}
32/// }
33/// ```
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
35pub enum StatusCode {
36 // ---- Success (2xx) ----
37 /// 200: Operation succeeded with changes applied.
38 Ok,
39 /// 204: Operation succeeded but no changes were made (target already in desired state).
40 NoChange,
41 /// 206: Operation succeeded with automatic conflict resolution.
42 Resolved,
43
44 // ---- Client Error (4xx) ----
45 /// 400: Invalid goal or planning failed.
46 Invalid,
47 /// 404: Target symbol or file not found.
48 NotFound,
49 /// 409: Conflicts detected that require manual resolution.
50 Conflict,
51 /// 422: Syntax validation failed after mutation.
52 SyntaxError,
53}
54
55impl StatusCode {
56 /// Returns true if this is a success status (2xx equivalent).
57 pub fn is_success(&self) -> bool {
58 matches!(self, Self::Ok | Self::NoChange | Self::Resolved)
59 }
60
61 /// Returns true if this is an error status (4xx equivalent).
62 pub fn is_error(&self) -> bool {
63 !self.is_success()
64 }
65
66 /// Returns the HTTP-like numeric code for reference.
67 pub fn as_http_code(&self) -> u16 {
68 match self {
69 Self::Ok => 200,
70 Self::NoChange => 204,
71 Self::Resolved => 206,
72 Self::Invalid => 400,
73 Self::NotFound => 404,
74 Self::Conflict => 409,
75 Self::SyntaxError => 422,
76 }
77 }
78
79 /// Returns the standard label for this status code.
80 pub fn label(&self) -> &'static str {
81 match self {
82 Self::Ok => "OK",
83 Self::NoChange => "NO_CHANGE",
84 Self::Resolved => "RESOLVED",
85 Self::Invalid => "INVALID",
86 Self::NotFound => "NOT_FOUND",
87 Self::Conflict => "CONFLICT",
88 Self::SyntaxError => "SYNTAX_ERROR",
89 }
90 }
91}
92
93impl std::fmt::Display for StatusCode {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 write!(f, "{}", self.label())
96 }
97}
98
99/// Detailed information about the execution status.
100///
101/// Provides rich context for both success and error cases,
102/// enabling informative CLI output and debugging.
103#[derive(Debug, Clone, Default, Serialize, Deserialize)]
104pub struct StatusDetail {
105 /// Short reason (one line, suitable for status line).
106 /// Example: "3 changes in 2 files"
107 /// Example: "Symbol 'foo' not found in registry"
108 pub reason: String,
109
110 /// Detailed explanation (can be multi-line).
111 /// Example: "The rename operation completed successfully.\nAll references were updated."
112 /// Example: "The pattern 'foo*' matched 0 symbols.\nTry 'ryo discover foo*' to see available symbols."
113 pub explanation: Option<String>,
114
115 /// Actionable suggestions for the user.
116 /// Example: ["Try 'ryo discover <pattern>' to find symbols", "Check spelling of symbol name"]
117 pub suggestions: Vec<String>,
118
119 /// Related context (e.g., conflicting symbols, matched patterns).
120 pub context: Vec<String>,
121}
122
123impl StatusDetail {
124 /// Create a new StatusDetail with just a reason.
125 pub fn new(reason: impl Into<String>) -> Self {
126 Self {
127 reason: reason.into(),
128 ..Default::default()
129 }
130 }
131
132 /// Add an explanation.
133 pub fn with_explanation(mut self, explanation: impl Into<String>) -> Self {
134 self.explanation = Some(explanation.into());
135 self
136 }
137
138 /// Add a suggestion.
139 pub fn with_suggestion(mut self, suggestion: impl Into<String>) -> Self {
140 self.suggestions.push(suggestion.into());
141 self
142 }
143
144 /// Add multiple suggestions.
145 pub fn with_suggestions(
146 mut self,
147 suggestions: impl IntoIterator<Item = impl Into<String>>,
148 ) -> Self {
149 self.suggestions
150 .extend(suggestions.into_iter().map(Into::into));
151 self
152 }
153
154 /// Add context information.
155 pub fn with_context(mut self, context: impl Into<String>) -> Self {
156 self.context.push(context.into());
157 self
158 }
159}
160
161/// Complete execution status combining code and detail.
162///
163/// This is the primary type for representing execution outcomes.
164/// It provides both machine-readable status codes and human-readable details.
165///
166/// # Example
167///
168/// ```ignore
169/// let status = ExecutionStatus::ok("3 changes in 2 files")
170/// .with_explanation("Renamed 'foo' to 'bar' across the codebase.");
171///
172/// if status.is_success() {
173/// println!("{}: {}", status.code, status.detail.reason);
174/// }
175/// ```
176#[derive(Debug, Clone, Serialize, Deserialize)]
177pub struct ExecutionStatus {
178 /// The status code.
179 pub code: StatusCode,
180 /// Detailed information.
181 pub detail: StatusDetail,
182}
183
184impl ExecutionStatus {
185 /// Create a new ExecutionStatus.
186 pub fn new(code: StatusCode, detail: StatusDetail) -> Self {
187 Self { code, detail }
188 }
189
190 // ---- Success constructors ----
191
192 /// Create an OK status (200).
193 pub fn ok(reason: impl Into<String>) -> Self {
194 Self::new(StatusCode::Ok, StatusDetail::new(reason))
195 }
196
197 /// Create a NO_CHANGE status (204).
198 pub fn no_change(reason: impl Into<String>) -> Self {
199 Self::new(StatusCode::NoChange, StatusDetail::new(reason))
200 }
201
202 /// Create a RESOLVED status (206).
203 pub fn resolved(reason: impl Into<String>) -> Self {
204 Self::new(StatusCode::Resolved, StatusDetail::new(reason))
205 }
206
207 // ---- Error constructors ----
208
209 /// Create an INVALID status (400).
210 pub fn invalid(reason: impl Into<String>) -> Self {
211 Self::new(StatusCode::Invalid, StatusDetail::new(reason))
212 }
213
214 /// Create a NOT_FOUND status (404).
215 pub fn not_found(reason: impl Into<String>) -> Self {
216 Self::new(StatusCode::NotFound, StatusDetail::new(reason))
217 }
218
219 /// Create a CONFLICT status (409).
220 pub fn conflict(reason: impl Into<String>) -> Self {
221 Self::new(StatusCode::Conflict, StatusDetail::new(reason))
222 }
223
224 /// Create a SYNTAX_ERROR status (422).
225 pub fn syntax_error(reason: impl Into<String>) -> Self {
226 Self::new(StatusCode::SyntaxError, StatusDetail::new(reason))
227 }
228
229 // ---- Builder methods ----
230
231 /// Add an explanation.
232 pub fn with_explanation(mut self, explanation: impl Into<String>) -> Self {
233 self.detail.explanation = Some(explanation.into());
234 self
235 }
236
237 /// Add a suggestion.
238 pub fn with_suggestion(mut self, suggestion: impl Into<String>) -> Self {
239 self.detail.suggestions.push(suggestion.into());
240 self
241 }
242
243 /// Add multiple suggestions.
244 pub fn with_suggestions(
245 mut self,
246 suggestions: impl IntoIterator<Item = impl Into<String>>,
247 ) -> Self {
248 self.detail
249 .suggestions
250 .extend(suggestions.into_iter().map(Into::into));
251 self
252 }
253
254 /// Add context.
255 pub fn with_context(mut self, context: impl Into<String>) -> Self {
256 self.detail.context.push(context.into());
257 self
258 }
259
260 // ---- Query methods ----
261
262 /// Returns true if this is a success status.
263 pub fn is_success(&self) -> bool {
264 self.code.is_success()
265 }
266
267 /// Returns true if this is an error status.
268 pub fn is_error(&self) -> bool {
269 self.code.is_error()
270 }
271}
272
273impl std::fmt::Display for ExecutionStatus {
274 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
275 write!(f, "{}: {}", self.code, self.detail.reason)
276 }
277}
278
279// ============================================================================
280// Discover
281// ============================================================================
282
283/// Sort order for discovery results.
284#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
285pub enum SortOrder {
286 /// Sort by name alphabetically.
287 Alpha,
288 /// Sort by reference count (most referenced first).
289 #[default]
290 Refs,
291 /// Sort by impl count.
292 Impls,
293}
294
295/// Request for symbol discovery.
296#[derive(Debug, Clone, Serialize, Deserialize)]
297pub struct DiscoverRequest {
298 /// Pattern to search for (glob-like: `*`, `?`, `[a-z]`).
299 pub pattern: String,
300 /// Filter by item kind (function, struct, enum, etc.).
301 pub kind: Option<SymbolKind>,
302 /// Sort order for results.
303 pub sort: Option<SortOrder>,
304 /// Maximum number of results to return.
305 pub limit: Option<usize>,
306 /// Output detail level (snippet, precise, count, def, full).
307 #[serde(default)]
308 pub view: Option<ViewMode>,
309 /// Filter for async functions only (true = async only, false = non-async only).
310 #[serde(default)]
311 pub is_async: Option<bool>,
312 /// Filter for unsafe functions/traits (true = unsafe only, false = safe only).
313 #[serde(default)]
314 pub is_unsafe: Option<bool>,
315 /// Filter by file path pattern (glob, e.g. "src/handlers/**").
316 #[serde(default)]
317 pub scope_path: Option<String>,
318 /// Case-insensitive pattern matching (A-Z == a-z).
319 #[serde(default)]
320 pub ignore_case: bool,
321 /// Ignore word separators and casing style (snake_case == camelCase == PascalCase).
322 #[serde(default)]
323 pub ignore_word_separate: bool,
324 /// Filter by attribute pattern (e.g., "deprecated", "allow(dead_code)").
325 #[serde(default)]
326 pub attr: Option<String>,
327 /// Interpret pattern as SymbolId instead of name pattern.
328 ///
329 /// When true, the `pattern` field is parsed as a SymbolId (e.g., "165v1")
330 /// and a direct registry lookup is performed instead of pattern matching.
331 #[serde(default)]
332 pub is_id: bool,
333}
334
335impl Default for DiscoverRequest {
336 fn default() -> Self {
337 Self {
338 pattern: "*".to_string(),
339 kind: None,
340 sort: None,
341 limit: None,
342 view: None,
343 is_async: None,
344 is_unsafe: None,
345 scope_path: None,
346 ignore_case: false,
347 ignore_word_separate: false,
348 attr: None,
349 is_id: false,
350 }
351 }
352}
353
354/// A discovered symbol (tarpc-compatible, Bincode-safe).
355#[derive(Debug, Clone, Serialize, Deserialize)]
356pub struct DiscoveredSymbol {
357 /// SymbolId as string (e.g., "165v1")
358 ///
359 /// **Warning**: This ID is session-volatile. Use `uuid` for persistent references.
360 pub id: String,
361 /// Persistent UUID for cross-session symbol tracking.
362 ///
363 /// This UUID survives server restarts and symbol renames.
364 /// Returns `None` if the symbol hasn't been assigned a persistent ID.
365 #[serde(default, skip_serializing_if = "Option::is_none")]
366 pub uuid: Option<String>,
367 /// Full symbol path (e.g., "ryo_analysis::registry::DetectRegistry")
368 pub path: String,
369 /// Symbol name
370 pub name: String,
371 /// Item kind (e.g., "Function", "Struct")
372 pub kind: String,
373 /// View mode used
374 pub view_mode: String,
375 /// Definition (for Def/Full modes)
376 #[serde(default, skip_serializing_if = "Option::is_none")]
377 pub definition: Option<String>,
378 /// Documentation (for Def/Full modes)
379 #[serde(default, skip_serializing_if = "Option::is_none")]
380 pub doc: Option<String>,
381 /// Function body (for Full mode)
382 #[serde(default, skip_serializing_if = "Option::is_none")]
383 pub body: Option<String>,
384 /// Code snippet (for Snippet mode)
385 #[serde(default, skip_serializing_if = "Option::is_none")]
386 pub snippet: Option<String>,
387}
388
389/// Response from symbol discovery (tarpc-compatible, Bincode-safe).
390#[derive(Debug, Clone, Serialize, Deserialize)]
391pub struct DiscoverResponse {
392 /// Query status
393 pub status: String,
394 /// Discovered symbols
395 pub symbols: Vec<DiscoveredSymbol>,
396 /// Total matches
397 pub total: usize,
398 /// Elapsed time in ms
399 pub elapsed_ms: u32,
400 /// Alternative search hint when 0 results with kind filter
401 #[serde(default, skip_serializing_if = "Option::is_none")]
402 pub hint: Option<String>,
403}
404
405// ============================================================================
406// Overview
407// ============================================================================
408
409/// Request for codebase overview.
410#[derive(Debug, Clone, Default, Serialize, Deserialize)]
411pub struct OverviewRequest {}
412
413/// A crate with its module tree.
414#[derive(Debug, Clone, Serialize, Deserialize)]
415pub struct CrateOverview {
416 /// Crate name.
417 pub name: String,
418 /// Module paths within this crate.
419 pub modules: Vec<String>,
420}
421
422/// A symbol with a count metric (ref_count or impl_count).
423#[derive(Debug, Clone, Serialize, Deserialize)]
424pub struct OverviewSymbol {
425 /// Full symbol path.
426 pub path: String,
427 /// Metric count (references or implementations).
428 pub count: usize,
429}
430
431/// Symbol kind statistics.
432#[derive(Debug, Clone, Serialize, Deserialize)]
433pub struct OverviewStats {
434 /// Number of crates.
435 pub crate_count: usize,
436 /// Counts by symbol kind (e.g., [("Functions", 120), ("Structs", 45)]).
437 pub by_kind: Vec<(String, usize)>,
438}
439
440/// Response from codebase overview.
441#[derive(Debug, Clone, Serialize, Deserialize)]
442pub struct OverviewResponse {
443 /// Crate/module structure.
444 pub crates: Vec<CrateOverview>,
445 /// Top structs by reference count.
446 pub top_structs: Vec<OverviewSymbol>,
447 /// Top traits by implementation count.
448 pub top_traits: Vec<OverviewSymbol>,
449 /// Statistics.
450 pub stats: OverviewStats,
451 /// Elapsed time in ms.
452 pub elapsed_ms: u32,
453}
454
455// ============================================================================
456// Literal Search
457// ============================================================================
458
459/// Request for literal search.
460#[derive(Debug, Clone, Serialize, Deserialize)]
461pub struct LiteralSearchRequest {
462 /// Pattern to search for (glob-style: `*error*`, `"hello"`, etc.).
463 pub pattern: String,
464 /// Filter by literal kind (e.g., "string", "int", "bool").
465 #[serde(default)]
466 pub kind: Option<String>,
467 /// Maximum number of results.
468 #[serde(default = "default_literal_limit")]
469 pub limit: usize,
470}
471
472fn default_literal_limit() -> usize {
473 100
474}
475
476/// A single literal match result.
477#[derive(Debug, Clone, Serialize, Deserialize)]
478pub struct LiteralMatchResult {
479 /// The literal value as it appears in source.
480 pub value: String,
481 /// The literal kind (e.g., "String", "Int", "Bool").
482 pub kind: String,
483 /// SymbolId of the containing symbol.
484 pub symbol_id: String,
485 /// File path containing this literal.
486 pub file_path: String,
487 /// Search relevance score.
488 pub score: f32,
489}
490
491/// Response from literal search.
492#[derive(Debug, Clone, Serialize, Deserialize)]
493pub struct LiteralSearchResponse {
494 /// Matched literals.
495 pub matches: Vec<LiteralMatchResult>,
496 /// Total matches found.
497 pub total: usize,
498 /// Elapsed time in ms.
499 pub elapsed_ms: u32,
500}
501
502// ============================================================================
503// RyoQL Query
504// ============================================================================
505
506/// Request for RyoQL query execution.
507#[derive(Debug, Clone, Serialize, Deserialize)]
508pub struct RyoqlRequest {
509 /// The RyoQL query content (YAML or JSON string).
510 pub query: String,
511 /// Default view mode when the query itself does not specify one.
512 #[serde(default)]
513 pub default_view: Option<ViewMode>,
514}
515
516// Note: Response type is `QueryResponse` re-exported from ryo-query-language (line 12).
517
518// ============================================================================
519// Suggest
520// ============================================================================
521
522/// Request for code improvement suggestions.
523#[derive(Debug, Clone, Default, Serialize, Deserialize)]
524pub struct SuggestRequest {
525 /// Optional pattern to filter suggestions.
526 pub pattern_filter: Option<String>,
527 /// Only show high-impact suggestions.
528 pub high_impact: bool,
529 /// Quick mode (fewer but faster suggestions).
530 pub quick: bool,
531 /// Run scan before returning suggestions.
532 /// When true, executes suggest_scan() to detect new suggestions.
533 pub scan: bool,
534 /// Run pre-check on each suggestion during scan.
535 /// When true, uses suggest_scan_with_precheck() which validates
536 /// each suggestion before storing, ensuring Apply will succeed.
537 #[serde(default)]
538 pub precheck: bool,
539 /// Rule IDs to exclude (e.g., ["RL021", "RL020"]).
540 #[serde(default)]
541 pub exclude_rules: Vec<String>,
542 /// Return enhanced suggestions with design choices and verification status.
543 #[serde(default)]
544 pub enhanced: bool,
545 /// Scope filter: only include suggestions from these scopes (lib, bin, test).
546 /// Empty means include all scopes.
547 #[serde(default)]
548 pub scope_filter: Vec<String>,
549}
550
551/// A code improvement suggestion.
552#[derive(Debug, Clone, Serialize, Deserialize)]
553pub struct Suggestion {
554 /// Unique identifier for the suggestion.
555 pub id: String,
556 /// Rule ID for @spec:allow directive (e.g., "RL001").
557 pub rule_id: Option<String>,
558 /// Human-readable title.
559 pub title: String,
560 /// Detailed description.
561 pub description: String,
562 /// Category (refactoring, performance, readability, etc.).
563 pub category: String,
564 /// Impact level (high, medium, low).
565 pub impact: String,
566 /// File where the suggestion applies.
567 pub file: PathBuf,
568 /// Symbol path for precise location (e.g., "crate::module::MyStruct").
569 #[serde(default, skip_serializing_if = "Option::is_none")]
570 pub symbol_path: Option<String>,
571 /// The Intent that would fix this issue.
572 pub fix_intent: Option<String>,
573}
574
575/// Summary of suggestions.
576#[derive(Debug, Clone, Default, Serialize, Deserialize)]
577pub struct SuggestionSummary {
578 /// Total number of suggestions.
579 pub total: usize,
580 /// Number of high-impact suggestions.
581 pub high_impact: usize,
582 /// Categories with counts.
583 pub by_category: Vec<(String, usize)>,
584}
585
586/// Response from suggestion engine.
587#[derive(Debug, Clone, Serialize, Deserialize, Default)]
588pub struct SuggestResponse {
589 /// List of suggestions.
590 pub suggestions: Vec<Suggestion>,
591 /// Summary statistics.
592 pub summary: SuggestionSummary,
593 /// Enhanced suggestions (when request.enhanced = true).
594 /// Contains design choices, verification status, and apply commands.
595 #[serde(default, skip_serializing_if = "Vec::is_empty")]
596 pub enhanced: Vec<EnhancedSuggestion>,
597}
598
599// ============================================================================
600// Suggest Apply
601// ============================================================================
602
603/// Request to apply suggestions by ID.
604#[derive(Debug, Clone, Serialize, Deserialize)]
605pub struct SuggestApplyRequest {
606 /// Suggestion IDs to apply (e.g., ["S001g0", "S002g0"]).
607 pub ids: Vec<String>,
608 /// Skip actual execution (preview only).
609 pub dry_run: bool,
610 /// Verify syntax after mutations.
611 pub check_syntax: bool,
612 /// Specific design choice to apply (e.g., "A", "B").
613 /// If None, uses the recommended choice.
614 #[serde(default)]
615 pub choice_id: Option<String>,
616 /// Run full verification (cargo check) before applying.
617 #[serde(default)]
618 pub verify: bool,
619}
620
621impl SuggestApplyRequest {
622 /// Create a new apply request with suggestion IDs.
623 pub fn new(ids: Vec<String>) -> Self {
624 Self {
625 ids,
626 dry_run: false,
627 check_syntax: false,
628 choice_id: None,
629 verify: false,
630 }
631 }
632
633 /// Enable dry-run mode.
634 pub fn dry_run(mut self) -> Self {
635 self.dry_run = true;
636 self
637 }
638
639 /// Enable syntax checking.
640 pub fn check_syntax(mut self) -> Self {
641 self.check_syntax = true;
642 self
643 }
644
645 /// Set a specific design choice to apply.
646 pub fn choice(mut self, choice_id: impl Into<String>) -> Self {
647 self.choice_id = Some(choice_id.into());
648 self
649 }
650
651 /// Enable verification before applying.
652 pub fn verify(mut self) -> Self {
653 self.verify = true;
654 self
655 }
656}
657
658/// Response from applying suggestions.
659#[derive(Debug, Clone, Serialize, Deserialize, Default)]
660pub struct SuggestApplyResponse {
661 /// Whether execution completed successfully.
662 pub success: bool,
663 /// Number of files modified.
664 pub files_modified: usize,
665 /// Total number of changes made.
666 pub total_changes: usize,
667 /// Paths of modified files.
668 pub modified_files: Vec<PathBuf>,
669 /// Suggestions that were successfully applied.
670 pub applied_ids: Vec<String>,
671 /// Suggestions that failed to apply (with error messages).
672 pub failed: Vec<(String, String)>,
673 /// Syntax errors found (if check_syntax enabled).
674 pub syntax_errors: Vec<String>,
675 /// Error message (if completely failed).
676 pub error: Option<String>,
677}
678
679// ============================================================================
680// Suggest Choices
681// ============================================================================
682
683/// Request to get design choices for a suggestion.
684#[derive(Debug, Clone, Serialize, Deserialize)]
685pub struct SuggestChoicesRequest {
686 /// Suggestion ID (e.g., "S001g0").
687 pub id: String,
688}
689
690impl SuggestChoicesRequest {
691 /// Create a new choices request.
692 pub fn new(id: impl Into<String>) -> Self {
693 Self { id: id.into() }
694 }
695}
696
697/// A design choice option.
698#[derive(Debug, Clone, Serialize, Deserialize)]
699pub struct DesignChoiceInfo {
700 /// Choice ID (e.g., "A", "B").
701 pub id: String,
702 /// Short label for display.
703 pub label: String,
704 /// Human-readable title.
705 pub title: String,
706 /// Detailed description.
707 pub description: String,
708 /// Extensibility rating (1-3 stars).
709 pub extensibility: u8,
710 /// Performance rating (1-3 stars).
711 pub performance: u8,
712 /// Complexity rating (1-3 stars).
713 pub complexity: u8,
714 /// Whether this is a breaking change.
715 pub breaking_change: bool,
716 /// Number of files affected.
717 pub affected_files: usize,
718 /// Whether this is the recommended choice.
719 pub recommended: bool,
720}
721
722/// Response with design choices for a suggestion.
723#[derive(Debug, Clone, Serialize, Deserialize, Default)]
724pub struct SuggestChoicesResponse {
725 /// Suggestion ID.
726 pub suggestion_id: String,
727 /// Pattern name that generated these choices.
728 pub pattern_name: String,
729 /// Available choices.
730 pub choices: Vec<DesignChoiceInfo>,
731 /// Whether choices are available.
732 pub has_choices: bool,
733 /// Error message if failed.
734 pub error: Option<String>,
735}
736
737// ============================================================================
738// Suggest Verify
739// ============================================================================
740
741/// Verification level.
742#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
743pub enum VerifyLevel {
744 /// Fast in-memory check (GraphChecker, ~100ms).
745 #[default]
746 Light,
747 /// Full cargo check in temp workspace.
748 Full,
749}
750
751/// Request to verify a suggestion before applying.
752#[derive(Debug, Clone, Serialize, Deserialize)]
753pub struct SuggestVerifyRequest {
754 /// Suggestion ID (e.g., "S001g0").
755 pub id: String,
756 /// Specific choice to verify (if multiple choices exist).
757 #[serde(default)]
758 pub choice_id: Option<String>,
759 /// Verification level.
760 #[serde(default)]
761 pub level: VerifyLevel,
762}
763
764impl SuggestVerifyRequest {
765 /// Create a new verify request.
766 pub fn new(id: impl Into<String>) -> Self {
767 Self {
768 id: id.into(),
769 choice_id: None,
770 level: VerifyLevel::default(),
771 }
772 }
773
774 /// Set a specific choice to verify.
775 pub fn choice(mut self, choice_id: impl Into<String>) -> Self {
776 self.choice_id = Some(choice_id.into());
777 self
778 }
779
780 /// Set verification level.
781 pub fn level(mut self, level: VerifyLevel) -> Self {
782 self.level = level;
783 self
784 }
785}
786
787/// Response from verification.
788#[derive(Debug, Clone, Serialize, Deserialize, Default)]
789pub struct SuggestVerifyResponse {
790 /// Suggestion ID.
791 pub suggestion_id: String,
792 /// Choice ID (if specified).
793 pub choice_id: Option<String>,
794 /// Whether verification passed.
795 pub passed: bool,
796 /// Verification level used.
797 pub level: String,
798 /// Duration in milliseconds.
799 pub duration_ms: u64,
800 /// Diagnostics (errors/warnings).
801 pub diagnostics: Vec<String>,
802 /// Error message if failed.
803 pub error: Option<String>,
804}
805
806// ============================================================================
807// Suggest Compare
808// ============================================================================
809
810/// Request to compare design choices.
811#[derive(Debug, Clone, Serialize, Deserialize)]
812pub struct SuggestCompareRequest {
813 /// Suggestion ID (e.g., "S001g0").
814 pub id: String,
815}
816
817impl SuggestCompareRequest {
818 /// Create a new compare request.
819 pub fn new(id: impl Into<String>) -> Self {
820 Self { id: id.into() }
821 }
822}
823
824/// Comparison result for choices.
825#[derive(Debug, Clone, Serialize, Deserialize, Default)]
826pub struct SuggestCompareResponse {
827 /// Suggestion ID.
828 pub suggestion_id: String,
829 /// Formatted comparison table.
830 pub comparison_table: String,
831 /// Choices sorted by score (best first).
832 pub ranked_choices: Vec<DesignChoiceInfo>,
833 /// Recommendation reason.
834 pub recommendation_reason: Option<String>,
835 /// Error message if failed.
836 pub error: Option<String>,
837}
838
839// ============================================================================
840// Suggest Generate
841// ============================================================================
842
843/// Request to generate code from parameterized patterns.
844///
845/// Use `list: true` to discover available patterns.
846/// Use `pattern` + `params` to generate code.
847#[derive(Debug, Clone, Default, Serialize, Deserialize)]
848pub struct SuggestGenerateRequest {
849 /// Pattern name to use (e.g., "api-pattern", "domain-struct").
850 pub pattern: Option<String>,
851 /// Parameters for the pattern as key-value pairs.
852 /// E.g., `{"name": "Order", "fields": "id:u64,status:String"}`.
853 #[serde(default)]
854 pub params: std::collections::HashMap<String, String>,
855 /// List available parameterized patterns instead of generating.
856 #[serde(default)]
857 pub list: bool,
858 /// Apply generated changes immediately (skip preview).
859 #[serde(default)]
860 pub apply: bool,
861 /// Skip actual execution (preview only).
862 #[serde(default)]
863 pub dry_run: bool,
864}
865
866impl SuggestGenerateRequest {
867 /// Create a request to list available patterns.
868 pub fn list_patterns() -> Self {
869 Self {
870 list: true,
871 ..Default::default()
872 }
873 }
874
875 /// Create a request to generate from a pattern.
876 pub fn generate(pattern: impl Into<String>) -> Self {
877 Self {
878 pattern: Some(pattern.into()),
879 ..Default::default()
880 }
881 }
882
883 /// Add a parameter.
884 pub fn with_param(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
885 self.params.insert(key.into(), value.into());
886 self
887 }
888
889 /// Set apply mode.
890 pub fn apply(mut self) -> Self {
891 self.apply = true;
892 self
893 }
894
895 /// Set dry-run mode.
896 pub fn dry_run(mut self) -> Self {
897 self.dry_run = true;
898 self
899 }
900}
901
902/// Information about a parameterized pattern.
903#[derive(Debug, Clone, Serialize, Deserialize)]
904pub struct PatternInfo {
905 /// Pattern name (e.g., "api-pattern").
906 pub name: String,
907 /// Human-readable description.
908 pub description: String,
909 /// Category (e.g., "Pattern").
910 pub category: String,
911 /// Parameters this pattern accepts.
912 pub params: Vec<ParamInfo>,
913}
914
915/// Information about a pattern parameter.
916#[derive(Debug, Clone, Serialize, Deserialize)]
917pub struct ParamInfo {
918 /// Parameter name (e.g., "name").
919 pub name: String,
920 /// Human-readable description.
921 pub description: String,
922 /// Whether this parameter is required.
923 pub required: bool,
924}
925
926/// Response from code generation.
927#[derive(Debug, Clone, Default, Serialize, Deserialize)]
928pub struct SuggestGenerateResponse {
929 /// Available patterns (when request.list = true).
930 #[serde(default, skip_serializing_if = "Vec::is_empty")]
931 pub patterns: Vec<PatternInfo>,
932 /// Generated suggestions (when pattern specified).
933 #[serde(default, skip_serializing_if = "Vec::is_empty")]
934 pub suggestions: Vec<Suggestion>,
935 /// Preview of changes (code diff).
936 #[serde(default, skip_serializing_if = "Option::is_none")]
937 pub preview: Option<String>,
938 /// Applied changes (when apply = true).
939 #[serde(default)]
940 pub applied: bool,
941 /// Number of files modified (when apply = true).
942 #[serde(default)]
943 pub files_modified: usize,
944 /// Error message if failed.
945 #[serde(default, skip_serializing_if = "Option::is_none")]
946 pub error: Option<String>,
947}
948
949// ============================================================================
950// Run (Execute)
951// ============================================================================
952
953/// Request for code transformation execution.
954#[derive(Debug, Clone, Serialize, Deserialize)]
955pub struct RunRequest {
956 /// The goal to execute.
957 pub goal: Goal,
958 /// Skip actual execution (plan and validate only).
959 pub dry_run: bool,
960 /// Verify syntax after mutations.
961 pub check_syntax: bool,
962}
963
964impl RunRequest {
965 /// Create a new run request with a goal.
966 pub fn new(goal: Goal) -> Self {
967 Self {
968 goal,
969 dry_run: false,
970 check_syntax: false,
971 }
972 }
973
974 /// Enable dry-run mode.
975 pub fn dry_run(mut self) -> Self {
976 self.dry_run = true;
977 self
978 }
979
980 /// Enable syntax checking.
981 pub fn check_syntax(mut self) -> Self {
982 self.check_syntax = true;
983 self
984 }
985}
986
987/// Response from code transformation execution.
988#[derive(Debug, Clone, Serialize, Deserialize, Default)]
989pub struct RunResponse {
990 /// Whether execution completed successfully.
991 pub success: bool,
992 /// Number of files modified.
993 pub files_modified: usize,
994 /// Total number of changes made.
995 pub total_changes: usize,
996 /// Paths of modified files.
997 pub modified_files: Vec<PathBuf>,
998 /// Conflict descriptions (if any).
999 pub conflicts: Vec<String>,
1000 /// Syntax errors found (if check_syntax enabled).
1001 pub syntax_errors: Vec<String>,
1002 /// Error message (if failed).
1003 pub error: Option<String>,
1004}
1005
1006// ============================================================================
1007// Graph Cascade
1008// ============================================================================
1009
1010/// Request for cascade analysis.
1011///
1012/// Use `ryo discover` to find the SymbolId or UUID.
1013#[derive(Debug, Clone, Serialize, Deserialize)]
1014pub struct CascadeRequest {
1015 /// SymbolId to analyze (e.g., "741v1").
1016 /// Get this from `ryo discover` output.
1017 /// **Warning**: Session-volatile. Prefer `uuid` for persistent references.
1018 pub id: String,
1019 /// Persistent UUID for cross-session symbol tracking.
1020 /// Takes precedence over `id` if provided.
1021 #[serde(default, skip_serializing_if = "Option::is_none")]
1022 pub uuid: Option<String>,
1023 /// Maximum depth to traverse (default: 3).
1024 pub depth: Option<usize>,
1025}
1026
1027impl CascadeRequest {
1028 /// Create a new cascade request with SymbolId.
1029 pub fn new(id: impl Into<String>) -> Self {
1030 Self {
1031 id: id.into(),
1032 uuid: None,
1033 depth: None,
1034 }
1035 }
1036
1037 /// Create a new cascade request with UUID.
1038 pub fn with_uuid(uuid: impl Into<String>) -> Self {
1039 Self {
1040 id: String::new(),
1041 uuid: Some(uuid.into()),
1042 depth: None,
1043 }
1044 }
1045
1046 /// Set the traversal depth.
1047 pub fn depth(mut self, depth: usize) -> Self {
1048 self.depth = Some(depth);
1049 self
1050 }
1051}
1052
1053/// Response from cascade analysis.
1054///
1055/// For detailed type impact analysis, use `graph type` instead.
1056#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1057pub struct CascadeResponse {
1058 /// Display name of the target symbol.
1059 pub display_name: String,
1060 /// Functions/methods that call the target.
1061 pub callers: Vec<String>,
1062 /// Types/functions that use the target as a type reference.
1063 pub users: Vec<String>,
1064 /// Functions containing match expressions on this enum.
1065 /// Only populated when the target is an Enum.
1066 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1067 pub match_functions: Vec<String>,
1068 /// Types that contain this type as a field.
1069 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1070 pub containing_types: Vec<String>,
1071}
1072
1073// ============================================================================
1074// Graph Summary
1075// ============================================================================
1076
1077/// Request for code graph summary.
1078#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1079pub struct GraphSummaryRequest {
1080 /// Detailed output with file paths and type breakdown.
1081 #[serde(default)]
1082 pub detailed: bool,
1083 /// Compact output for minimal display.
1084 #[serde(default)]
1085 pub compact: bool,
1086 /// Maximum items per section.
1087 #[serde(default)]
1088 pub max_items: Option<usize>,
1089}
1090
1091impl GraphSummaryRequest {
1092 /// Create a new default request.
1093 pub fn new() -> Self {
1094 Self::default()
1095 }
1096
1097 /// Enable detailed output.
1098 pub fn detailed(mut self) -> Self {
1099 self.detailed = true;
1100 self
1101 }
1102
1103 /// Enable compact output.
1104 pub fn compact(mut self) -> Self {
1105 self.compact = true;
1106 self
1107 }
1108
1109 /// Set max items per section.
1110 pub fn max_items(mut self, n: usize) -> Self {
1111 self.max_items = Some(n);
1112 self
1113 }
1114}
1115
1116/// Response from code graph summary.
1117#[derive(Debug, Clone, Serialize, Deserialize)]
1118pub struct GraphSummaryResponse {
1119 /// Formatted summary content (ready to print).
1120 pub content: String,
1121 /// Build time in milliseconds.
1122 pub build_time_ms: u64,
1123 /// Number of nodes in the graph.
1124 pub node_count: usize,
1125 /// Number of edges in the graph.
1126 pub edge_count: usize,
1127 /// Number of files analyzed.
1128 pub file_count: usize,
1129}
1130
1131// ============================================================================
1132// Ping
1133// ============================================================================
1134
1135/// Response from ping (health check with version).
1136#[derive(Debug, Clone, Serialize, Deserialize)]
1137pub struct PingResponse {
1138 /// Server version (from Cargo.toml).
1139 pub version: String,
1140}
1141
1142// ============================================================================
1143// Status
1144// ============================================================================
1145
1146/// Response from status query.
1147#[derive(Debug, Clone, Serialize, Deserialize)]
1148pub struct StatusResponse {
1149 /// Project root path.
1150 pub project: PathBuf,
1151 /// Number of symbols in registry.
1152 pub symbols: usize,
1153 /// Number of files loaded.
1154 pub files: usize,
1155}
1156
1157// ============================================================================
1158// Error Types (for RPC transport)
1159// ============================================================================
1160
1161/// Structured error type for API responses.
1162#[derive(Debug, Clone, Serialize, Deserialize)]
1163pub enum ApiErrorKind {
1164 /// Symbol or resource not found.
1165 NotFound { name: String },
1166 /// Parse or syntax error.
1167 ParseError { message: String },
1168 /// Invalid request parameters.
1169 InvalidRequest { message: String },
1170 /// Internal server error.
1171 Internal { message: String },
1172}
1173
1174impl std::fmt::Display for ApiErrorKind {
1175 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1176 match self {
1177 Self::NotFound { name } => write!(f, "not found: {}", name),
1178 Self::ParseError { message } => write!(f, "parse error: {}", message),
1179 Self::InvalidRequest { message } => write!(f, "invalid request: {}", message),
1180 Self::Internal { message } => write!(f, "internal error: {}", message),
1181 }
1182 }
1183}
1184
1185impl std::error::Error for ApiErrorKind {}
1186
1187// ============================================================================
1188// Spec
1189// ============================================================================
1190
1191/// Request for spec hierarchy query.
1192#[derive(Debug, Clone, Serialize, Deserialize)]
1193pub struct SpecRequest {
1194 /// Query kind.
1195 pub query: SpecQueryKind,
1196}
1197
1198/// Spec query kinds.
1199#[derive(Debug, Clone, Serialize, Deserialize)]
1200pub enum SpecQueryKind {
1201 /// Show full hierarchy (groups, relations, stats).
1202 Show,
1203 /// List all group names.
1204 Groups,
1205 /// List specs in a specific group.
1206 TypesInGroup { group: String },
1207 /// Get dependencies of a spec.
1208 Dependencies { type_name: String },
1209 /// Get dependents (reverse dependencies) of a spec.
1210 Dependents { type_name: String },
1211 /// Get statistics.
1212 Stats,
1213 /// Lint specs for consistency.
1214 Lint,
1215 /// Generate Mermaid diagram.
1216 Mermaid,
1217}
1218
1219impl SpecRequest {
1220 /// Create a show request.
1221 pub fn show() -> Self {
1222 Self {
1223 query: SpecQueryKind::Show,
1224 }
1225 }
1226
1227 /// Create a groups request.
1228 pub fn groups() -> Self {
1229 Self {
1230 query: SpecQueryKind::Groups,
1231 }
1232 }
1233
1234 /// Create a types-in-group request.
1235 pub fn types_in_group(group: impl Into<String>) -> Self {
1236 Self {
1237 query: SpecQueryKind::TypesInGroup {
1238 group: group.into(),
1239 },
1240 }
1241 }
1242
1243 /// Create a stats request.
1244 pub fn stats() -> Self {
1245 Self {
1246 query: SpecQueryKind::Stats,
1247 }
1248 }
1249
1250 /// Create a lint request.
1251 pub fn lint() -> Self {
1252 Self {
1253 query: SpecQueryKind::Lint,
1254 }
1255 }
1256
1257 /// Create a mermaid request.
1258 pub fn mermaid() -> Self {
1259 Self {
1260 query: SpecQueryKind::Mermaid,
1261 }
1262 }
1263}
1264
1265/// Response from spec query.
1266#[derive(Debug, Clone, Serialize, Deserialize)]
1267pub enum SpecResponse {
1268 /// Full hierarchy response.
1269 Show(SpecShowData),
1270 /// List of group names.
1271 Groups(Vec<String>),
1272 /// List of specs in a group.
1273 TypesInGroup(Vec<SpecInfoData>),
1274 /// Dependency list.
1275 Dependencies(Vec<String>),
1276 /// Dependent list.
1277 Dependents(Vec<String>),
1278 /// Statistics.
1279 Stats(SpecStatsData),
1280 /// Lint result.
1281 Lint(SpecLintData),
1282 /// Mermaid diagram.
1283 Mermaid(String),
1284}
1285
1286/// Full spec hierarchy data.
1287#[derive(Debug, Clone, Serialize, Deserialize)]
1288pub struct SpecShowData {
1289 /// All groups with their specs.
1290 pub groups: Vec<SpecGroupData>,
1291 /// All dependency relations.
1292 pub relations: Vec<SpecRelationData>,
1293 /// Statistics.
1294 pub stats: SpecStatsData,
1295}
1296
1297/// Group information.
1298#[derive(Debug, Clone, Serialize, Deserialize)]
1299pub struct SpecGroupData {
1300 /// Group name.
1301 pub name: String,
1302 /// Specs in this group.
1303 pub specs: Vec<SpecInfoData>,
1304}
1305
1306/// Spec information.
1307#[derive(Debug, Clone, Serialize, Deserialize)]
1308pub struct SpecInfoData {
1309 /// Alias name (e.g., "DbConfig").
1310 pub alias_name: String,
1311 /// Wrapped type name (e.g., "DatabaseConfig").
1312 pub wrapped_type_name: String,
1313 /// Source kind.
1314 pub source: String,
1315}
1316
1317/// Spec relation.
1318#[derive(Debug, Clone, Serialize, Deserialize)]
1319pub struct SpecRelationData {
1320 /// Source spec name.
1321 pub from: String,
1322 /// Target spec name.
1323 pub to: String,
1324 /// Relation kind.
1325 pub kind: String,
1326}
1327
1328/// Statistics.
1329#[derive(Debug, Clone, Serialize, Deserialize)]
1330pub struct SpecStatsData {
1331 /// Number of groups.
1332 pub groups: usize,
1333 /// Number of specs.
1334 pub specs: usize,
1335 /// Total node count.
1336 pub nodes: usize,
1337 /// Total edge count.
1338 pub edges: usize,
1339}
1340
1341/// Lint result data.
1342#[derive(Debug, Clone, Serialize, Deserialize)]
1343pub struct SpecLintData {
1344 /// All issues found.
1345 pub issues: Vec<SpecLintIssueData>,
1346 /// Number of warnings.
1347 pub warnings: usize,
1348 /// Number of errors.
1349 pub errors: usize,
1350}
1351
1352/// Lint issue data.
1353#[derive(Debug, Clone, Serialize, Deserialize)]
1354pub struct SpecLintIssueData {
1355 /// Severity ("warning" or "error").
1356 pub severity: String,
1357 /// Issue message.
1358 pub message: String,
1359 /// Optional location.
1360 pub location: Option<String>,
1361}
1362
1363// ============================================================================
1364// Graph Type Analysis
1365// ============================================================================
1366
1367/// Analysis mode for type relationships.
1368#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
1369pub enum TypeAnalysisMode {
1370 /// Show type definition details.
1371 Definition,
1372 /// Show all usages of this type.
1373 Usage,
1374 /// Show impact of changing this type.
1375 #[default]
1376 Impact,
1377}
1378
1379/// Request for type analysis.
1380#[derive(Debug, Clone, Serialize, Deserialize)]
1381pub struct TypeAnalysisRequest {
1382 /// Type name to analyze (used if id/uuid not provided).
1383 #[serde(default)]
1384 pub name: Option<String>,
1385 /// SymbolId to analyze directly (e.g., "165v1").
1386 /// **Warning**: Session-volatile. Prefer `uuid` for persistent references.
1387 #[serde(default)]
1388 pub id: Option<String>,
1389 /// Persistent UUID for cross-session symbol tracking.
1390 /// Takes precedence over `id` and `name` if provided.
1391 #[serde(default, skip_serializing_if = "Option::is_none")]
1392 pub uuid: Option<String>,
1393 /// Analysis mode.
1394 #[serde(default)]
1395 pub mode: TypeAnalysisMode,
1396 /// Maximum depth for traversal.
1397 #[serde(default)]
1398 pub depth: Option<usize>,
1399}
1400
1401impl TypeAnalysisRequest {
1402 /// Create a new type analysis request by name.
1403 pub fn new(name: impl Into<String>) -> Self {
1404 Self {
1405 name: Some(name.into()),
1406 id: None,
1407 uuid: None,
1408 mode: TypeAnalysisMode::default(),
1409 depth: None,
1410 }
1411 }
1412
1413 /// Create a new type analysis request by SymbolId.
1414 pub fn with_id(id: impl Into<String>) -> Self {
1415 Self {
1416 name: None,
1417 id: Some(id.into()),
1418 uuid: None,
1419 mode: TypeAnalysisMode::default(),
1420 depth: None,
1421 }
1422 }
1423
1424 /// Create a new type analysis request by UUID.
1425 pub fn with_uuid(uuid: impl Into<String>) -> Self {
1426 Self {
1427 name: None,
1428 id: None,
1429 uuid: Some(uuid.into()),
1430 mode: TypeAnalysisMode::default(),
1431 depth: None,
1432 }
1433 }
1434
1435 /// Set the analysis mode.
1436 pub fn mode(mut self, mode: TypeAnalysisMode) -> Self {
1437 self.mode = mode;
1438 self
1439 }
1440}
1441
1442/// Response from type analysis.
1443#[derive(Debug, Clone, Serialize, Deserialize)]
1444pub struct TypeAnalysisResponse {
1445 /// Symbol ID string.
1446 pub symbol_id: String,
1447 /// Display name.
1448 pub display_name: String,
1449 /// Module path.
1450 pub mod_path: Option<String>,
1451 /// Kind (Struct, Enum, etc.).
1452 pub kind: Option<String>,
1453 /// Usage count.
1454 pub usage_count: usize,
1455 /// Usages (context, ref_kind). Populated for Usage/Impact modes.
1456 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1457 pub usages: Vec<TypeUsageInfo>,
1458 /// Impact info (direct usages, bound usages, containing types). Populated for Impact mode.
1459 pub impact: Option<TypeImpactInfo>,
1460 /// Supertraits (for traits only): parent traits from `trait Foo: Bar + Baz`.
1461 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1462 pub supertraits: Vec<String>,
1463 /// Implementors (for traits only): types that implement this trait.
1464 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1465 pub implementors: Vec<String>,
1466 /// Struct/enum fields. Populated for Definition mode.
1467 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1468 pub fields: Vec<TypeFieldInfo>,
1469 /// Enum variants. Populated for Definition mode.
1470 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1471 pub variants: Vec<TypeVariantInfo>,
1472 /// Function parameters. Populated for Definition mode.
1473 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1474 pub params: Vec<TypeParamInfo>,
1475 /// Function return type. Populated for Definition mode.
1476 #[serde(default, skip_serializing_if = "Option::is_none")]
1477 pub return_type: Option<String>,
1478 /// Trait method names. Populated for Definition mode.
1479 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1480 pub methods: Vec<String>,
1481 /// Generic parameters string (e.g. "<T, U: Clone>"). Populated for Definition mode.
1482 #[serde(default, skip_serializing_if = "Option::is_none")]
1483 pub generics: Option<String>,
1484 /// Attributes (e.g. ["derive", "serde"]). Populated for Definition mode.
1485 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1486 pub attrs: Vec<String>,
1487}
1488
1489/// Field information for definition mode.
1490#[derive(Debug, Clone, Serialize, Deserialize)]
1491pub struct TypeFieldInfo {
1492 /// Field name.
1493 pub name: String,
1494 /// Type as string.
1495 pub ty: String,
1496 /// Is publicly visible.
1497 pub is_public: bool,
1498}
1499
1500/// Enum variant information for definition mode.
1501#[derive(Debug, Clone, Serialize, Deserialize)]
1502pub struct TypeVariantInfo {
1503 /// Variant name.
1504 pub name: String,
1505 /// Fields of this variant.
1506 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1507 pub fields: Vec<TypeFieldInfo>,
1508}
1509
1510/// Parameter information for definition mode.
1511#[derive(Debug, Clone, Serialize, Deserialize)]
1512pub struct TypeParamInfo {
1513 /// Parameter name.
1514 pub name: String,
1515 /// Type as string.
1516 pub ty: String,
1517}
1518
1519/// Type usage information.
1520#[derive(Debug, Clone, Serialize, Deserialize)]
1521pub struct TypeUsageInfo {
1522 /// Usage context.
1523 pub context: String,
1524 /// Reference kind.
1525 pub ref_kind: String,
1526 /// Container symbol path (the function/struct that uses this type).
1527 #[serde(default, skip_serializing_if = "Option::is_none")]
1528 pub container: Option<String>,
1529}
1530
1531/// Type impact information.
1532#[derive(Debug, Clone, Serialize, Deserialize)]
1533pub struct TypeImpactInfo {
1534 /// Direct usage count.
1535 pub direct_usages: usize,
1536 /// Bound usage count.
1537 pub bound_usages: usize,
1538 /// Containing types.
1539 pub containing_types: Vec<String>,
1540}
1541
1542// ============================================================================
1543// Graph Flow Analysis
1544// ============================================================================
1545
1546/// Analysis mode for data flow.
1547#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
1548pub enum FlowAnalysisMode {
1549 /// Track where values come from.
1550 Provenance,
1551 /// Track where values flow to.
1552 Impact,
1553 /// Full chain analysis (provenance + impact).
1554 #[default]
1555 Chain,
1556 /// Find data sources.
1557 Sources,
1558 /// Find data sinks.
1559 Sinks,
1560}
1561
1562/// Request for flow analysis.
1563#[derive(Debug, Clone, Serialize, Deserialize)]
1564pub struct FlowAnalysisRequest {
1565 /// Variable name to analyze (used if id/uuid not provided).
1566 #[serde(default)]
1567 pub name: Option<String>,
1568 /// SymbolId to analyze directly (e.g., "165v1").
1569 /// **Warning**: Session-volatile. Prefer `uuid` for persistent references.
1570 #[serde(default)]
1571 pub id: Option<String>,
1572 /// Persistent UUID for cross-session symbol tracking.
1573 /// Takes precedence over `id` and `name` if provided.
1574 #[serde(default, skip_serializing_if = "Option::is_none")]
1575 pub uuid: Option<String>,
1576 /// Analysis mode.
1577 #[serde(default)]
1578 pub mode: FlowAnalysisMode,
1579 /// Maximum depth for traversal.
1580 #[serde(default)]
1581 pub depth: Option<usize>,
1582}
1583
1584impl FlowAnalysisRequest {
1585 /// Create a new flow analysis request by name.
1586 pub fn new(name: impl Into<String>) -> Self {
1587 Self {
1588 name: Some(name.into()),
1589 id: None,
1590 uuid: None,
1591 mode: FlowAnalysisMode::default(),
1592 depth: None,
1593 }
1594 }
1595
1596 /// Create a new flow analysis request by SymbolId.
1597 pub fn with_id(id: impl Into<String>) -> Self {
1598 Self {
1599 name: None,
1600 id: Some(id.into()),
1601 uuid: None,
1602 mode: FlowAnalysisMode::default(),
1603 depth: None,
1604 }
1605 }
1606
1607 /// Create a new flow analysis request by UUID.
1608 pub fn with_uuid(uuid: impl Into<String>) -> Self {
1609 Self {
1610 name: None,
1611 id: None,
1612 uuid: Some(uuid.into()),
1613 mode: FlowAnalysisMode::default(),
1614 depth: None,
1615 }
1616 }
1617}
1618
1619/// Variable information.
1620#[derive(Debug, Clone, Serialize, Deserialize)]
1621pub struct VarInfo {
1622 /// Variable name.
1623 pub name: String,
1624 /// Variable kind.
1625 pub kind: String,
1626 /// Parent function.
1627 pub parent: String,
1628 /// Symbol path.
1629 pub symbol_path: Option<String>,
1630}
1631
1632/// Response from flow analysis.
1633#[derive(Debug, Clone, Serialize, Deserialize)]
1634pub struct FlowAnalysisResponse {
1635 /// Display name.
1636 pub display_name: String,
1637 /// Found variables.
1638 pub found_vars: Vec<VarInfo>,
1639 /// Provenance (sources).
1640 pub provenance: Vec<VarInfo>,
1641 /// Impact (targets).
1642 pub impact: Vec<VarInfo>,
1643 /// Sources (data origins).
1644 pub sources: Vec<VarInfo>,
1645 /// Sinks (data destinations).
1646 pub sinks: Vec<VarInfo>,
1647}
1648
1649// ============================================================================
1650// Graph Borrow Analysis
1651// ============================================================================
1652
1653/// Request for borrow analysis.
1654#[derive(Debug, Clone, Serialize, Deserialize)]
1655pub struct BorrowAnalysisRequest {
1656 /// Variable name to analyze (used if id/uuid not provided).
1657 #[serde(default)]
1658 pub name: Option<String>,
1659 /// SymbolId to analyze directly (e.g., "165v1").
1660 /// **Warning**: Session-volatile. Prefer `uuid` for persistent references.
1661 #[serde(default)]
1662 pub id: Option<String>,
1663 /// Persistent UUID for cross-session symbol tracking.
1664 /// Takes precedence over `id` and `name` if provided.
1665 #[serde(default, skip_serializing_if = "Option::is_none")]
1666 pub uuid: Option<String>,
1667 /// Show only conflicts.
1668 #[serde(default)]
1669 pub conflicts_only: bool,
1670}
1671
1672impl BorrowAnalysisRequest {
1673 /// Create a new borrow analysis request by name.
1674 pub fn new(name: impl Into<String>) -> Self {
1675 Self {
1676 name: Some(name.into()),
1677 id: None,
1678 uuid: None,
1679 conflicts_only: false,
1680 }
1681 }
1682
1683 /// Create a new borrow analysis request by SymbolId.
1684 pub fn with_id(id: impl Into<String>) -> Self {
1685 Self {
1686 name: None,
1687 id: Some(id.into()),
1688 uuid: None,
1689 conflicts_only: false,
1690 }
1691 }
1692
1693 /// Create a new borrow analysis request by UUID.
1694 pub fn with_uuid(uuid: impl Into<String>) -> Self {
1695 Self {
1696 name: None,
1697 id: None,
1698 uuid: Some(uuid.into()),
1699 conflicts_only: false,
1700 }
1701 }
1702}
1703
1704/// Borrow status for a variable.
1705#[derive(Debug, Clone, Serialize, Deserialize)]
1706pub struct BorrowStatus {
1707 /// Variable name.
1708 pub var_name: String,
1709 /// Line number.
1710 pub line: u32,
1711 /// Parent function.
1712 pub parent_info: String,
1713 /// Has conflict.
1714 pub has_conflict: bool,
1715 /// Conflict errors.
1716 pub errors: Vec<String>,
1717}
1718
1719/// Response from borrow analysis.
1720#[derive(Debug, Clone, Serialize, Deserialize)]
1721pub struct BorrowAnalysisResponse {
1722 /// Display name.
1723 pub display_name: String,
1724 /// Found variables count.
1725 pub found_count: usize,
1726 /// Borrow statuses.
1727 pub statuses: Vec<BorrowStatus>,
1728}
1729
1730// ============================================================================
1731// Graph Lock Analysis
1732// ============================================================================
1733
1734/// Request for lock analysis.
1735#[derive(Debug, Clone, Serialize, Deserialize)]
1736pub struct LockAnalysisRequest {
1737 /// Lock name pattern (used if id/uuid not provided).
1738 #[serde(default)]
1739 pub name: Option<String>,
1740 /// SymbolId to analyze directly (e.g., "165v1").
1741 /// **Warning**: Session-volatile. Prefer `uuid` for persistent references.
1742 #[serde(default)]
1743 pub id: Option<String>,
1744 /// Persistent UUID for cross-session symbol tracking.
1745 /// Takes precedence over `id` and `name` if provided.
1746 #[serde(default, skip_serializing_if = "Option::is_none")]
1747 pub uuid: Option<String>,
1748 /// Show optimization suggestions.
1749 #[serde(default)]
1750 pub suggest: bool,
1751}
1752
1753impl LockAnalysisRequest {
1754 /// Create a new lock analysis request by name.
1755 pub fn new(name: impl Into<String>) -> Self {
1756 Self {
1757 name: Some(name.into()),
1758 id: None,
1759 uuid: None,
1760 suggest: false,
1761 }
1762 }
1763
1764 /// Create a new lock analysis request by SymbolId.
1765 pub fn with_id(id: impl Into<String>) -> Self {
1766 Self {
1767 name: None,
1768 id: Some(id.into()),
1769 uuid: None,
1770 suggest: false,
1771 }
1772 }
1773
1774 /// Create a new lock analysis request by UUID.
1775 pub fn with_uuid(uuid: impl Into<String>) -> Self {
1776 Self {
1777 name: None,
1778 id: None,
1779 uuid: Some(uuid.into()),
1780 suggest: false,
1781 }
1782 }
1783}
1784
1785/// Lock statistics.
1786#[derive(Debug, Clone, Serialize, Deserialize)]
1787pub struct LockStats {
1788 /// Total locks.
1789 pub total_locks: u32,
1790 /// Mutex count.
1791 pub mutex_count: u32,
1792 /// RwLock count.
1793 pub rwlock_count: u32,
1794 /// RefCell count.
1795 pub refcell_count: u32,
1796 /// Total field accesses.
1797 pub total_field_accesses: u32,
1798 /// Max critical section span.
1799 pub max_cs_span: u32,
1800}
1801
1802/// Lock acquisition info.
1803#[derive(Debug, Clone, Serialize, Deserialize)]
1804pub struct LockAcquisition {
1805 /// Lock name.
1806 pub lock_name: String,
1807 /// Lock type.
1808 pub lock_type: String,
1809 /// Line number.
1810 pub line: u32,
1811}
1812
1813/// Lock suggestion.
1814#[derive(Debug, Clone, Serialize, Deserialize)]
1815pub struct LockSuggestionInfo {
1816 /// Suggestion kind.
1817 pub kind: String,
1818 /// Target name.
1819 pub target: String,
1820 /// Description.
1821 pub description: String,
1822 /// Line number.
1823 pub line: u32,
1824}
1825
1826/// Response from lock analysis.
1827#[derive(Debug, Clone, Serialize, Deserialize)]
1828pub struct LockAnalysisResponse {
1829 /// Display name.
1830 pub display_name: String,
1831 /// Lock statistics.
1832 pub stats: LockStats,
1833 /// Matching acquisitions.
1834 pub acquisitions: Vec<LockAcquisition>,
1835 /// Suggestions (if requested).
1836 pub suggestions: Vec<LockSuggestionInfo>,
1837}
1838
1839// ============================================================================
1840// Graph Chain Analysis (Transitive Call Chain)
1841// ============================================================================
1842
1843/// Traversal mode for chain analysis.
1844///
1845/// - `Callers`/`Callees`: Call chain traversal (CodeGraphV2)
1846/// - `TypeUsers`/`TypeDeps`: Type reference chain traversal (TypeFlowGraphV2)
1847#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
1848pub enum ChainMode {
1849 /// Follow incoming call edges (who calls this function?)
1850 #[default]
1851 Callers,
1852 /// Follow outgoing call edges (what does this function call?)
1853 Callees,
1854 /// Follow type reference edges: who uses this type? (type → containers)
1855 TypeUsers,
1856 /// Follow type reference edges: what types does this use? (container → types)
1857 TypeDeps,
1858}
1859
1860impl std::fmt::Display for ChainMode {
1861 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1862 match self {
1863 ChainMode::Callers => write!(f, "callers"),
1864 ChainMode::Callees => write!(f, "callees"),
1865 ChainMode::TypeUsers => write!(f, "type_users"),
1866 ChainMode::TypeDeps => write!(f, "type_deps"),
1867 }
1868 }
1869}
1870
1871/// Request for chain analysis.
1872///
1873/// Traverses call relationships transitively to find all callers or callees
1874/// up to a specified depth.
1875#[derive(Debug, Clone, Serialize, Deserialize)]
1876pub struct ChainAnalysisRequest {
1877 /// SymbolId to analyze (e.g., "165v1").
1878 /// Get this from `ryo discover` output.
1879 /// **Warning**: Session-volatile. Prefer `uuid` for persistent references.
1880 pub id: String,
1881 /// Persistent UUID for cross-session symbol tracking.
1882 /// Takes precedence over `id` if provided.
1883 #[serde(default, skip_serializing_if = "Option::is_none")]
1884 pub uuid: Option<String>,
1885 /// Traversal mode.
1886 #[serde(default)]
1887 pub mode: ChainMode,
1888 /// Maximum traversal depth (default: 5).
1889 #[serde(default)]
1890 pub depth: Option<usize>,
1891}
1892
1893impl ChainAnalysisRequest {
1894 /// Create a new chain analysis request with SymbolId.
1895 pub fn new(id: impl Into<String>) -> Self {
1896 Self {
1897 id: id.into(),
1898 uuid: None,
1899 mode: ChainMode::default(),
1900 depth: None,
1901 }
1902 }
1903
1904 /// Create a new chain analysis request with UUID.
1905 pub fn with_uuid(uuid: impl Into<String>) -> Self {
1906 Self {
1907 id: String::new(),
1908 uuid: Some(uuid.into()),
1909 mode: ChainMode::default(),
1910 depth: None,
1911 }
1912 }
1913
1914 /// Set the traversal mode.
1915 pub fn mode(mut self, mode: ChainMode) -> Self {
1916 self.mode = mode;
1917 self
1918 }
1919
1920 /// Set the traversal depth.
1921 pub fn depth(mut self, depth: usize) -> Self {
1922 self.depth = Some(depth);
1923 self
1924 }
1925}
1926
1927/// A node in the chain with depth information.
1928#[derive(Debug, Clone, Serialize, Deserialize)]
1929pub struct ChainNodeInfo {
1930 /// SymbolId as string.
1931 pub id: String,
1932 /// Full symbol path.
1933 pub path: String,
1934 /// Symbol kind (Function, Struct, etc.).
1935 pub kind: Option<String>,
1936 /// Depth from starting symbol.
1937 pub depth: usize,
1938}
1939
1940/// Response from chain analysis.
1941#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1942pub struct ChainAnalysisResponse {
1943 /// Display name of the starting symbol.
1944 pub display_name: String,
1945 /// Traversal direction used.
1946 pub direction: String,
1947 /// Total count of nodes in the chain.
1948 pub total_count: usize,
1949 /// Maximum depth actually reached.
1950 pub max_actual_depth: usize,
1951 /// Nodes grouped by depth level.
1952 pub by_depth: std::collections::BTreeMap<usize, Vec<ChainNodeInfo>>,
1953}