Skip to main content

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}