Skip to main content

opencode_sdk/http/
misc.rs

1//! Miscellaneous API endpoints for OpenCode.
2//!
3//! Includes: VCS, path, instance, log, LSP, formatter, global endpoints.
4
5use crate::error::Result;
6use crate::http::HttpClient;
7use crate::types::api::{FormatterInfo, LspServerStatus, OpenApiDoc};
8use reqwest::Method;
9use serde::{Deserialize, Serialize};
10
11/// Misc API client.
12#[derive(Clone)]
13pub struct MiscApi {
14    http: HttpClient,
15}
16
17impl MiscApi {
18    /// Create a new Misc API client.
19    pub fn new(http: HttpClient) -> Self {
20        Self { http }
21    }
22
23    /// Get current path info.
24    ///
25    /// # Errors
26    ///
27    /// Returns an error if the request fails.
28    pub async fn path(&self) -> Result<PathInfo> {
29        self.http.request_json(Method::GET, "/path", None).await
30    }
31
32    /// Get VCS info.
33    ///
34    /// # Errors
35    ///
36    /// Returns an error if the request fails.
37    pub async fn vcs(&self) -> Result<VcsInfo> {
38        self.http.request_json(Method::GET, "/vcs", None).await
39    }
40
41    /// Dispose instance.
42    ///
43    /// # Errors
44    ///
45    /// Returns an error if the request fails.
46    pub async fn dispose(&self) -> Result<()> {
47        self.http
48            .request_empty(
49                Method::POST,
50                "/instance/dispose",
51                Some(serde_json::json!({})),
52            )
53            .await
54    }
55
56    /// Write log entry.
57    ///
58    /// # Errors
59    ///
60    /// Returns an error if the request fails.
61    pub async fn log(&self, entry: &LogEntry) -> Result<()> {
62        let body = serde_json::to_value(entry)?;
63        self.http
64            .request_empty(Method::POST, "/log", Some(body))
65            .await
66    }
67
68    /// Get LSP server status for all configured LSP servers.
69    ///
70    /// # Errors
71    ///
72    /// Returns an error if the request fails.
73    pub async fn lsp(&self) -> Result<Vec<LspServerStatus>> {
74        self.http.request_json(Method::GET, "/lsp", None).await
75    }
76
77    /// Get formatter status for all configured formatters.
78    ///
79    /// # Errors
80    ///
81    /// Returns an error if the request fails.
82    pub async fn formatter(&self) -> Result<Vec<FormatterInfo>> {
83        self.http
84            .request_json(Method::GET, "/formatter", None)
85            .await
86    }
87
88    /// Get global health.
89    ///
90    /// # Errors
91    ///
92    /// Returns an error if the request fails.
93    pub async fn health(&self) -> Result<HealthInfo> {
94        self.http
95            .request_json(Method::GET, "/global/health", None)
96            .await
97    }
98
99    /// Dispose global.
100    ///
101    /// # Errors
102    ///
103    /// Returns an error if the request fails.
104    pub async fn global_dispose(&self) -> Result<()> {
105        self.http
106            .request_empty(Method::POST, "/global/dispose", Some(serde_json::json!({})))
107            .await
108    }
109
110    /// Get OpenAPI spec.
111    ///
112    /// # Errors
113    ///
114    /// Returns an error if the request fails.
115    pub async fn doc(&self) -> Result<OpenApiDoc> {
116        self.http.request_json(Method::GET, "/doc", None).await
117    }
118}
119
120/// Path information.
121#[derive(Debug, Clone, Serialize, Deserialize)]
122#[serde(rename_all = "camelCase")]
123pub struct PathInfo {
124    /// Current directory.
125    pub directory: String,
126    /// Project root.
127    #[serde(default, skip_serializing_if = "Option::is_none")]
128    pub project_root: Option<String>,
129}
130
131/// VCS information.
132#[derive(Debug, Clone, Serialize, Deserialize)]
133#[serde(rename_all = "camelCase")]
134pub struct VcsInfo {
135    /// VCS type (git, etc.).
136    #[serde(default, skip_serializing_if = "Option::is_none")]
137    pub r#type: Option<String>,
138    /// Current branch.
139    #[serde(default, skip_serializing_if = "Option::is_none")]
140    pub branch: Option<String>,
141    /// Remote URL.
142    #[serde(default, skip_serializing_if = "Option::is_none")]
143    pub remote: Option<String>,
144}
145
146/// Log entry.
147// TODO(3): Consider using enum for `level` field (Debug/Info/Warn/Error) with #[serde(other)] for forward-compat
148#[derive(Debug, Clone, Serialize, Deserialize)]
149#[serde(rename_all = "camelCase")]
150pub struct LogEntry {
151    /// Log level.
152    pub level: String,
153    /// Log message.
154    pub message: String,
155    /// Additional data.
156    #[serde(default, skip_serializing_if = "Option::is_none")]
157    pub data: Option<serde_json::Value>,
158}
159
160/// Health information.
161#[derive(Debug, Clone, Serialize, Deserialize)]
162#[serde(rename_all = "camelCase")]
163pub struct HealthInfo {
164    /// Whether healthy.
165    pub healthy: bool,
166    /// Server version.
167    #[serde(default, skip_serializing_if = "Option::is_none")]
168    pub version: Option<String>,
169}