Skip to main content

lean_ctx/
models.rs

1use serde::{Deserialize, Serialize};
2use serde_json::{Map, Value};
3
4#[derive(Debug, Clone, Default, Serialize, Deserialize)]
5pub struct RepositoryFingerprint {
6    pub remote_url: Option<String>,
7    pub host: Option<String>,
8    pub owner: Option<String>,
9    pub repo_name: Option<String>,
10    pub default_branch: Option<String>,
11}
12
13impl RepositoryFingerprint {
14    pub fn has_safe_identity(&self) -> bool {
15        self.remote_url
16            .as_ref()
17            .is_some_and(|value| !value.trim().is_empty())
18            || (self
19                .host
20                .as_ref()
21                .is_some_and(|value| !value.trim().is_empty())
22                && self
23                    .owner
24                    .as_ref()
25                    .is_some_and(|value| !value.trim().is_empty())
26                && self
27                    .repo_name
28                    .as_ref()
29                    .is_some_and(|value| !value.trim().is_empty()))
30    }
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct CheckoutBinding {
35    pub project_id: String,
36    pub local_root: Option<String>,
37    pub branch: Option<String>,
38    pub last_commit: Option<String>,
39    pub client_label: Option<String>,
40    pub last_sync: Option<String>,
41}
42
43impl Default for CheckoutBinding {
44    fn default() -> Self {
45        Self {
46            project_id: "pending".to_string(),
47            local_root: None,
48            branch: None,
49            last_commit: None,
50            client_label: None,
51            last_sync: None,
52        }
53    }
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct ProjectResolutionRequest {
58    pub fingerprint: RepositoryFingerprint,
59    pub suggested_slug: Option<String>,
60    #[serde(rename = "checkout_binding", alias = "workspace_binding")]
61    pub checkout_binding: Option<CheckoutBinding>,
62    pub project_metadata: Option<ProjectMetadataEnvelope>,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct ProjectRecord {
67    pub project_id: String,
68    pub slug: String,
69    pub fingerprint: Option<RepositoryFingerprint>,
70    pub created_at: String,
71    pub updated_at: String,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct ProjectResolutionResponse {
76    pub project: ProjectRecord,
77    #[serde(rename = "checkout_bound", alias = "workspace_bound")]
78    pub checkout_bound: bool,
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct ToolDefinition {
83    pub name: String,
84    pub description: String,
85    #[serde(rename = "inputSchema")]
86    pub input_schema: Value,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct ToolListResponse {
91    pub tools: Vec<ToolDefinition>,
92    pub total: usize,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct ToolCallRequest {
97    pub name: String,
98    pub arguments: Map<String, Value>,
99    pub project_id: Option<String>,
100    pub project_slug: Option<String>,
101    pub repository_fingerprint: Option<RepositoryFingerprint>,
102    #[serde(rename = "checkout_binding", alias = "workspace_binding")]
103    pub checkout_binding: Option<CheckoutBinding>,
104    pub project_metadata: Option<ProjectMetadataEnvelope>,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct ToolCallResponse {
109    pub result: Value,
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct ServerConnection {
114    pub endpoint: String,
115    pub token: String,
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct ProjectMetadataEnvelope {
120    pub schema_version: u32,
121    pub summary: ProjectMetadataSummary,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct ProjectMetadataSummary {
126    pub total_file_count: u64,
127    pub source_file_count: u64,
128    pub markers: Vec<String>,
129    pub languages: Vec<ProjectLanguageStat>,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct ProjectLanguageStat {
134    pub language: String,
135    pub file_count: u64,
136}
137
138#[derive(Debug, Clone)]
139pub struct ProjectContext {
140    pub project_slug: String,
141    pub project_root: String,
142    pub fingerprint: RepositoryFingerprint,
143    pub checkout_binding: CheckoutBinding,
144    pub project_metadata: Option<ProjectMetadataEnvelope>,
145}
146
147pub type RepositoryContext = ProjectContext;
148pub type WorkspaceBinding = CheckoutBinding;
149/// Request payload for POST /v1/telemetry/ingest.
150/// Only token counts and metadata — no raw file content or shell output.
151#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct TelemetryIngestRequest {
153    pub tool_name: String,
154    pub tokens_original: i64,
155    pub tokens_saved: i64,
156    pub duration_ms: i64,
157    #[serde(skip_serializing_if = "Option::is_none")]
158    pub mode: Option<String>,
159    #[serde(skip_serializing_if = "Option::is_none")]
160    pub repository_fingerprint: Option<RepositoryFingerprint>,
161    #[serde(skip_serializing_if = "Option::is_none")]
162    pub checkout_binding: Option<CheckoutBinding>,
163    #[serde(skip_serializing_if = "Option::is_none")]
164    pub project_slug: Option<String>,
165}