shopify_approver_opencode/
models.rs

1//! Data models for OpenCode API
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7/// Request to create a new codebase index
8#[derive(Debug, Clone, Serialize)]
9pub struct IndexRequest {
10    /// Optional name for the index
11    pub name: Option<String>,
12    /// Metadata to attach to the index
13    pub metadata: HashMap<String, String>,
14}
15
16impl IndexRequest {
17    pub fn new() -> Self {
18        Self {
19            name: None,
20            metadata: HashMap::new(),
21        }
22    }
23
24    pub fn with_name(mut self, name: impl Into<String>) -> Self {
25        self.name = Some(name.into());
26        self
27    }
28
29    pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
30        self.metadata.insert(key.into(), value.into());
31        self
32    }
33}
34
35impl Default for IndexRequest {
36    fn default() -> Self {
37        Self::new()
38    }
39}
40
41/// Response from creating an index
42#[derive(Debug, Clone, Deserialize)]
43pub struct IndexResponse {
44    /// Unique index ID
45    pub id: String,
46    /// Index status
47    pub status: IndexStatus,
48    /// When the index was created
49    pub created_at: DateTime<Utc>,
50    /// Number of files indexed
51    pub file_count: Option<usize>,
52    /// Total size of indexed content
53    pub total_size: Option<usize>,
54    /// Error message if indexing failed
55    pub error: Option<String>,
56}
57
58/// Status of a codebase index
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
60#[serde(rename_all = "lowercase")]
61pub enum IndexStatus {
62    /// Index is being created
63    Pending,
64    /// Files are being processed
65    Processing,
66    /// Index is ready for queries
67    Ready,
68    /// Indexing failed
69    Failed,
70    /// Index has been deleted
71    Deleted,
72}
73
74impl IndexStatus {
75    pub fn is_ready(&self) -> bool {
76        matches!(self, Self::Ready)
77    }
78
79    pub fn is_pending(&self) -> bool {
80        matches!(self, Self::Pending | Self::Processing)
81    }
82
83    pub fn is_failed(&self) -> bool {
84        matches!(self, Self::Failed)
85    }
86}
87
88/// Search request for semantic code search
89#[derive(Debug, Clone, Serialize)]
90pub struct SearchRequest {
91    /// Search query (natural language or code pattern)
92    pub query: String,
93    /// Maximum number of results
94    #[serde(default = "default_limit")]
95    pub limit: usize,
96    /// Minimum similarity score (0.0 - 1.0)
97    #[serde(default)]
98    pub min_score: Option<f32>,
99    /// Filter by file extensions
100    #[serde(default)]
101    pub extensions: Vec<String>,
102    /// Filter by file paths (glob patterns)
103    #[serde(default)]
104    pub paths: Vec<String>,
105    /// Include surrounding context
106    #[serde(default = "default_context_lines")]
107    pub context_lines: usize,
108}
109
110fn default_limit() -> usize {
111    10
112}
113
114fn default_context_lines() -> usize {
115    3
116}
117
118impl SearchRequest {
119    pub fn new(query: impl Into<String>) -> Self {
120        Self {
121            query: query.into(),
122            limit: default_limit(),
123            min_score: None,
124            extensions: Vec::new(),
125            paths: Vec::new(),
126            context_lines: default_context_lines(),
127        }
128    }
129
130    pub fn with_limit(mut self, limit: usize) -> Self {
131        self.limit = limit;
132        self
133    }
134
135    pub fn with_min_score(mut self, score: f32) -> Self {
136        self.min_score = Some(score);
137        self
138    }
139
140    pub fn with_extensions(mut self, extensions: Vec<String>) -> Self {
141        self.extensions = extensions;
142        self
143    }
144
145    pub fn with_paths(mut self, paths: Vec<String>) -> Self {
146        self.paths = paths;
147        self
148    }
149
150    pub fn with_context_lines(mut self, lines: usize) -> Self {
151        self.context_lines = lines;
152        self
153    }
154}
155
156/// Response from a search request
157#[derive(Debug, Clone, Deserialize)]
158pub struct SearchResponse {
159    /// Search results
160    pub results: Vec<CodeChunk>,
161    /// Total number of matches (may be more than returned)
162    pub total: usize,
163    /// Time taken to search (in milliseconds)
164    pub took_ms: u64,
165}
166
167/// A chunk of code from search results
168#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct CodeChunk {
170    /// File path relative to codebase root
171    pub file_path: String,
172    /// Start line number (1-indexed)
173    pub start_line: usize,
174    /// End line number (1-indexed)
175    pub end_line: usize,
176    /// The code content
177    pub content: String,
178    /// Similarity score (0.0 - 1.0)
179    pub score: f32,
180    /// Type of code element (function, class, etc.)
181    pub element_type: Option<String>,
182    /// Name of the code element
183    pub element_name: Option<String>,
184    /// Programming language
185    pub language: Option<String>,
186    /// Surrounding context (if requested)
187    pub context: Option<CodeContext>,
188}
189
190/// Surrounding context for a code chunk
191#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct CodeContext {
193    /// Lines before the match
194    pub before: Vec<String>,
195    /// Lines after the match
196    pub after: Vec<String>,
197    /// Import statements in the file
198    pub imports: Vec<String>,
199    /// Parent element (class name, module, etc.)
200    pub parent: Option<String>,
201}
202
203/// Request to get file context
204#[derive(Debug, Clone, Serialize)]
205pub struct ContextRequest {
206    /// File path to get context for
207    pub file_path: String,
208    /// Include related files (imports, dependencies)
209    #[serde(default = "default_include_related")]
210    pub include_related: bool,
211    /// Maximum depth for related files
212    #[serde(default = "default_related_depth")]
213    pub related_depth: usize,
214}
215
216fn default_include_related() -> bool {
217    true
218}
219
220fn default_related_depth() -> usize {
221    2
222}
223
224impl ContextRequest {
225    pub fn new(file_path: impl Into<String>) -> Self {
226        Self {
227            file_path: file_path.into(),
228            include_related: default_include_related(),
229            related_depth: default_related_depth(),
230        }
231    }
232
233    pub fn with_related(mut self, include: bool) -> Self {
234        self.include_related = include;
235        self
236    }
237
238    pub fn with_depth(mut self, depth: usize) -> Self {
239        self.related_depth = depth;
240        self
241    }
242}
243
244/// Response with file context
245#[derive(Debug, Clone, Deserialize)]
246pub struct ContextResponse {
247    /// The main file
248    pub file: FileInfo,
249    /// Related files (if requested)
250    pub related: Vec<FileInfo>,
251    /// Dependency graph
252    pub dependencies: Vec<Dependency>,
253}
254
255/// Information about an indexed file
256#[derive(Debug, Clone, Serialize, Deserialize)]
257pub struct FileInfo {
258    /// File path relative to codebase root
259    pub path: String,
260    /// File content
261    pub content: String,
262    /// Programming language
263    pub language: String,
264    /// File size in bytes
265    pub size: usize,
266    /// Number of lines
267    pub lines: usize,
268    /// AST summary (if available)
269    pub ast_summary: Option<AstSummary>,
270}
271
272/// Summary of AST analysis
273#[derive(Debug, Clone, Serialize, Deserialize)]
274pub struct AstSummary {
275    /// Number of functions/methods
276    pub function_count: usize,
277    /// Number of classes
278    pub class_count: usize,
279    /// Number of imports
280    pub import_count: usize,
281    /// Exported symbols
282    pub exports: Vec<String>,
283    /// Function signatures
284    pub functions: Vec<FunctionSignature>,
285}
286
287/// Function signature information
288#[derive(Debug, Clone, Serialize, Deserialize)]
289pub struct FunctionSignature {
290    /// Function name
291    pub name: String,
292    /// Line number
293    pub line: usize,
294    /// Parameters (if available)
295    pub params: Vec<String>,
296    /// Return type (if available)
297    pub return_type: Option<String>,
298    /// Is async function
299    pub is_async: bool,
300    /// Is exported
301    pub is_exported: bool,
302}
303
304/// Dependency between files
305#[derive(Debug, Clone, Serialize, Deserialize)]
306pub struct Dependency {
307    /// Source file
308    pub from: String,
309    /// Target file
310    pub to: String,
311    /// Type of dependency (import, require, etc.)
312    pub dep_type: String,
313    /// Imported symbols
314    pub symbols: Vec<String>,
315}
316
317/// Statistics about an index
318#[derive(Debug, Clone, Deserialize)]
319pub struct IndexStats {
320    /// Total number of files
321    pub file_count: usize,
322    /// Total size in bytes
323    pub total_size: usize,
324    /// Files by language
325    pub languages: HashMap<String, usize>,
326    /// Average file size
327    pub avg_file_size: usize,
328    /// Number of indexed chunks
329    pub chunk_count: usize,
330    /// Index creation time
331    pub created_at: DateTime<Utc>,
332    /// Last update time
333    pub updated_at: DateTime<Utc>,
334}