1use anyhow::Result;
7use serde::{Deserialize, Serialize};
8use serde_json::Value;
9use std::fs;
10use chrono::Utc;
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct SnapshotMeta {
15 pub name: String,
16 pub recorded_at: String,
17 pub server_name: String,
18 pub server_version: String,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct ToolInfo {
23 pub name: String,
24 pub description: String,
25 #[serde(rename = "inputSchema")]
26 pub input_schema: Option<Value>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30#[serde(rename_all = "camelCase")]
31pub struct Exchange {
32 pub id: String,
33 pub tool: String,
34 pub input: Value,
35 pub match_mode: String,
36 pub expected: ExchangeResult,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct ExchangeResult {
41 pub is_error: bool,
42 pub content: Vec<Value>,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct Snapshot {
47 pub format_version: u32,
48 pub meta: SnapshotMeta,
49 pub tool_catalog: Vec<ToolInfo>,
50 pub exchanges: Vec<Exchange>,
51}
52
53impl Snapshot {
54 pub fn new(server_name: String, server_version: String) -> Self {
55 Snapshot {
56 format_version: 1,
57 meta: SnapshotMeta {
58 name: format!("{}-baseline", server_name),
59 recorded_at: Utc::now().to_rfc3339(),
60 server_name,
61 server_version,
62 },
63 tool_catalog: Vec::new(),
64 exchanges: Vec::new(),
65 }
66 }
67
68 pub fn add_tool(&mut self, tool: ToolInfo) {
69 self.tool_catalog.push(tool);
70 }
71
72 pub fn add_exchange(&mut self, tool: String, input: Value, result: ExchangeResult) {
73 let id = format!("ex-{:03}", self.exchanges.len() + 1);
74 self.exchanges.push(Exchange {
75 id,
76 tool,
77 input,
78 match_mode: "structural".to_string(),
79 expected: result,
80 });
81 }
82
83 pub fn save(&self, path: &str) -> Result<()> {
84 let json = serde_json::to_string_pretty(&self)?;
85 fs::write(path, json)?;
86 println!("Snapshot saved to: {}", path);
87 Ok(())
88 }
89
90 pub fn load(path: &str) -> Result<Self> {
91 let content = fs::read_to_string(path)?;
92 let snapshot = serde_json::from_str(&content)?;
93 Ok(snapshot)
94 }
95}