Skip to main content

greplm_core/
proto.rs

1//! Wire protocol shared by the daemon and its clients.
2//!
3//! Messages are newline-delimited JSON over a Unix domain socket. Each request
4//! is one JSON object on a line; each response is one JSON object on a line.
5
6use std::path::PathBuf;
7
8use serde::{Deserialize, Serialize};
9
10use crate::search::{SearchQuery, SymbolQuery};
11
12/// Default socket path relative to a project's `.greplm` directory.
13pub const SOCKET_NAME: &str = "greplmd.sock";
14
15/// Machine-wide (per-user) socket for the global multi-root daemon. One daemon
16/// serves every project the user touches, lazily loading and evicting them, so
17/// running many agents across many repos costs a single background process.
18///
19/// Placed in the per-user runtime/temp dir so it's private to the user and
20/// cleared across reboots. Falls back through `XDG_RUNTIME_DIR` (Linux),
21/// `TMPDIR` (macOS), `~/.cache`, then the system temp dir.
22pub fn global_socket_path() -> PathBuf {
23    let base = std::env::var_os("XDG_RUNTIME_DIR")
24        .map(PathBuf::from)
25        .or_else(|| std::env::var_os("TMPDIR").map(PathBuf::from))
26        .or_else(|| std::env::var_os("HOME").map(|h| PathBuf::from(h).join(".cache")))
27        .unwrap_or_else(std::env::temp_dir);
28    base.join("greplm").join(SOCKET_NAME)
29}
30
31/// A request addressed to a specific project root, used by the global daemon
32/// (which serves many roots over one socket). The client resolves `root` to the
33/// project's `.greplm` ancestor before sending.
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct RoutedRequest {
36    pub root: PathBuf,
37    pub req: Request,
38}
39
40/// A request from a client to the daemon.
41#[derive(Debug, Clone, Serialize, Deserialize)]
42#[serde(tag = "op", rename_all = "snake_case")]
43pub enum Request {
44    Ping,
45    Status,
46    Summary,
47    Reindex {
48        force: bool,
49    },
50    Search(SearchQuery),
51    Symbols(SymbolQuery),
52    Refs {
53        name: String,
54        limit: usize,
55        offset: usize,
56    },
57    /// Resolved references (definitions + call sites + imports) from the
58    /// structural reference index.
59    RefsResolved {
60        name: String,
61        limit: usize,
62        offset: usize,
63    },
64    /// Call sites that target a symbol (who calls it).
65    Callers {
66        name: String,
67        limit: usize,
68        offset: usize,
69    },
70    /// Call sites inside a symbol's body (what it calls).
71    Callees {
72        name: String,
73        limit: usize,
74        offset: usize,
75    },
76    /// Symbols transitively affected by changing a symbol (reverse call graph).
77    BlastRadius {
78        name: String,
79        depth: u32,
80        limit: usize,
81    },
82    /// Typed go-to-definition for the identifier at a source position.
83    Definition {
84        file: String,
85        line: u32,
86        col: u32,
87    },
88    /// Resolved references for the identifier at a source position.
89    ReferencesAt {
90        file: String,
91        line: u32,
92        col: u32,
93    },
94    /// Structural (AST) search by tree-sitter query or meta-variable pattern.
95    Structural {
96        pattern: String,
97        lang: String,
98        limit: usize,
99        offset: usize,
100    },
101    /// Build a token-budgeted context pack for a task.
102    ContextPack {
103        task: String,
104        budget: u64,
105    },
106    /// Git blame for a single line.
107    Blame {
108        file: String,
109        line: u32,
110    },
111    /// Commit history of a symbol's definition.
112    History {
113        name: String,
114        limit: usize,
115    },
116    /// Files (with symbols) changed since a revision.
117    ChangedSince {
118        rev: String,
119    },
120    Outline {
121        file: String,
122    },
123    Snippet {
124        file: String,
125        start: u32,
126        end: u32,
127        context: u32,
128    },
129}
130
131/// A response from the daemon to a client.
132#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct Response {
134    pub ok: bool,
135    #[serde(skip_serializing_if = "Option::is_none")]
136    pub result: Option<serde_json::Value>,
137    #[serde(skip_serializing_if = "Option::is_none")]
138    pub error: Option<String>,
139}
140
141impl Response {
142    pub fn ok(value: serde_json::Value) -> Self {
143        Response {
144            ok: true,
145            result: Some(value),
146            error: None,
147        }
148    }
149
150    pub fn err(message: impl Into<String>) -> Self {
151        Response {
152            ok: false,
153            result: None,
154            error: Some(message.into()),
155        }
156    }
157}