use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub const PROTOCOL_VERSION: u32 = 1;
#[derive(Debug, Serialize, Deserialize)]
pub struct VcsPluginRequest {
pub method: String,
pub params: serde_json::Value,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct VcsPluginResponse {
pub ok: bool,
#[serde(default)]
pub result: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
impl VcsPluginResponse {
pub fn success(result: serde_json::Value) -> Self {
Self {
ok: true,
result,
error: None,
}
}
pub fn error(msg: impl Into<String>) -> Self {
Self {
ok: false,
result: serde_json::Value::Null,
error: Some(msg.into()),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct HandshakeParams {
pub ta_version: String,
pub protocol_version: u32,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct HandshakeResult {
pub plugin_version: String,
pub protocol_version: u32,
pub adapter_name: String,
#[serde(default)]
pub capabilities: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct DetectParams {
pub project_root: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct DetectResult {
pub detected: bool,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ExcludePatternsResult {
pub patterns: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SaveStateResult {
pub state: serde_json::Value,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct RestoreStateParams {
pub state: serde_json::Value,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PrepareParams {
pub goal_id: String,
pub goal_title: String,
pub workspace_path: String,
pub branch_prefix: String,
#[serde(default)]
pub co_author: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CommitParams {
pub goal_id: String,
pub goal_title: String,
pub message: String,
pub changed_files: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CommitResult {
pub commit_id: String,
pub message: String,
#[serde(default)]
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PushParams {
pub goal_id: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PushResult {
pub remote_ref: String,
pub message: String,
#[serde(default)]
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct OpenReviewParams {
pub goal_id: String,
pub goal_title: String,
pub draft_summary: String,
pub changed_files: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct OpenReviewResult {
pub review_url: String,
pub review_id: String,
pub message: String,
#[serde(default)]
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct RevisionIdResult {
pub revision_id: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ProtectedTargetsResult {
pub targets: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SyncUpstreamResult {
pub updated: bool,
pub conflicts: Vec<String>,
pub new_commits: u32,
pub message: String,
#[serde(default)]
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CheckReviewParams {
pub review_id: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CheckReviewResult {
pub found: bool,
#[serde(default)]
pub state: String,
#[serde(default)]
pub checks_passing: Option<bool>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct MergeReviewParams {
pub review_id: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct MergeReviewResult {
pub merged: bool,
#[serde(default)]
pub merge_commit: Option<String>,
pub message: String,
#[serde(default)]
pub metadata: HashMap<String, String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn request_roundtrip() {
let req = VcsPluginRequest {
method: "handshake".to_string(),
params: serde_json::json!({
"ta_version": "0.13.5-alpha",
"protocol_version": 1
}),
};
let json = serde_json::to_string(&req).unwrap();
let parsed: VcsPluginRequest = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.method, "handshake");
}
#[test]
fn response_success_roundtrip() {
let resp = VcsPluginResponse::success(serde_json::json!({"adapter_name": "perforce"}));
let json = serde_json::to_string(&resp).unwrap();
let parsed: VcsPluginResponse = serde_json::from_str(&json).unwrap();
assert!(parsed.ok);
assert!(parsed.error.is_none());
}
#[test]
fn response_error_roundtrip() {
let resp = VcsPluginResponse::error("p4 not found");
let json = serde_json::to_string(&resp).unwrap();
let parsed: VcsPluginResponse = serde_json::from_str(&json).unwrap();
assert!(!parsed.ok);
assert_eq!(parsed.error.as_deref(), Some("p4 not found"));
}
#[test]
fn handshake_params_roundtrip() {
let params = HandshakeParams {
ta_version: "0.13.5-alpha".to_string(),
protocol_version: PROTOCOL_VERSION,
};
let json = serde_json::to_string(¶ms).unwrap();
let parsed: HandshakeParams = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.protocol_version, 1);
}
#[test]
fn handshake_result_roundtrip() {
let result = HandshakeResult {
plugin_version: "0.1.0".to_string(),
protocol_version: 1,
adapter_name: "perforce".to_string(),
capabilities: vec![
"commit".to_string(),
"push".to_string(),
"protected_targets".to_string(),
],
};
let json = serde_json::to_string(&result).unwrap();
let parsed: HandshakeResult = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.adapter_name, "perforce");
assert!(parsed
.capabilities
.contains(&"protected_targets".to_string()));
}
#[test]
fn commit_params_roundtrip() {
let params = CommitParams {
goal_id: "abc123".to_string(),
goal_title: "Fix bug".to_string(),
message: "Fix critical bug\n\nCo-authored-by: test".to_string(),
changed_files: vec!["src/main.rs".to_string()],
};
let json = serde_json::to_string(¶ms).unwrap();
let parsed: CommitParams = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.goal_id, "abc123");
assert_eq!(parsed.changed_files.len(), 1);
}
#[test]
fn detect_params_roundtrip() {
let params = DetectParams {
project_root: "/home/user/project".to_string(),
};
let json = serde_json::to_string(¶ms).unwrap();
let parsed: DetectParams = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.project_root, "/home/user/project");
}
#[test]
fn protocol_version_is_one() {
assert_eq!(PROTOCOL_VERSION, 1);
}
}