Skip to main content

relay_knowledge/api/operations/
graph_canvas.rs

1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5use crate::api::ApiMetadata;
6
7pub const GRAPH_CANVAS_DEFAULT_LIMIT: usize = 250;
8pub const GRAPH_CANVAS_MAX_LIMIT: usize = 1000;
9
10/// Graph canvas view selected by the Web workspace.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12#[serde(rename_all = "snake_case")]
13pub enum GraphCanvasKind {
14    Knowledge,
15    Code,
16    Mixed,
17}
18
19impl GraphCanvasKind {
20    /// Parses the stable Web query representation.
21    pub fn parse(value: &str) -> Result<Self, String> {
22        match value {
23            "knowledge" => Ok(Self::Knowledge),
24            "code" => Ok(Self::Code),
25            "mixed" => Ok(Self::Mixed),
26            _ => Err(format!("unsupported graph canvas kind '{value}'")),
27        }
28    }
29
30    /// Returns the stable Web query representation.
31    pub const fn as_str(self) -> &'static str {
32        match self {
33            Self::Knowledge => "knowledge",
34            Self::Code => "code",
35            Self::Mixed => "mixed",
36        }
37    }
38}
39
40/// Bounded graph canvas request for same-origin Web exploration.
41#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
42pub struct GraphCanvasRequest {
43    pub kind: GraphCanvasKind,
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub source_scope: Option<String>,
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub query: Option<String>,
48    pub limit: usize,
49}
50
51/// Node rendered in the Web graph canvas.
52#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
53pub struct GraphCanvasNode {
54    pub id: String,
55    pub kind: String,
56    pub label: String,
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub subtitle: Option<String>,
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub source_scope: Option<String>,
61    pub graph_version: u64,
62    pub weight: u32,
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub status: Option<String>,
65    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
66    pub details: BTreeMap<String, String>,
67}
68
69/// Edge rendered in the Web graph canvas.
70#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
71pub struct GraphCanvasEdge {
72    pub id: String,
73    pub kind: String,
74    pub source: String,
75    pub target: String,
76    pub label: String,
77    pub graph_version: u64,
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub confidence_basis_points: Option<u16>,
80    #[serde(skip_serializing_if = "Option::is_none")]
81    pub evidence_count: Option<usize>,
82    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
83    pub details: BTreeMap<String, String>,
84}
85
86/// Bounded graph canvas summary for truncation and legend hints.
87#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
88pub struct GraphCanvasSummary {
89    pub kind: GraphCanvasKind,
90    pub node_count: usize,
91    pub edge_count: usize,
92    pub truncated: bool,
93    pub available_kinds: Vec<String>,
94}
95
96/// Same-origin graph canvas response.
97#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
98pub struct GraphCanvasResponse {
99    pub metadata: ApiMetadata,
100    pub nodes: Vec<GraphCanvasNode>,
101    pub edges: Vec<GraphCanvasEdge>,
102    pub summary: GraphCanvasSummary,
103}
104
105#[cfg(test)]
106#[path = "graph_canvas_tests.rs"]
107mod tests;