use serde::{Deserialize, Serialize};
use super::{require_schema, validate_text};
use crate::ValidationError;
pub const GRAPH_SCHEMA: &str = "leanctx.hive.graph/v1";
pub const MAX_GRAPH_NODES: usize = 20_000;
pub const MAX_GRAPH_EDGES: usize = 60_000;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum GraphNodeKind {
Code,
Knowledge,
Work,
Actor,
#[serde(other)]
Unknown,
}
pub const GRAPH_EDGE_TYPES: [&str; 8] = [
"applies_to",
"contains",
"imports",
"calls",
"shows",
"changed",
"worked_on",
"questions",
];
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GraphNodeV1 {
pub id: String,
pub kind: GraphNodeKind,
pub label: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GraphEdgeV1 {
pub from: String,
pub to: String,
#[serde(rename = "type")]
pub kind: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GraphV1 {
pub schema: String,
pub vault_id: String,
pub revision: u64,
pub nodes: Vec<GraphNodeV1>,
pub edges: Vec<GraphEdgeV1>,
}
impl GraphV1 {
pub fn validate(&self) -> Result<(), ValidationError> {
require_schema(&self.schema, GRAPH_SCHEMA)?;
super::validate_vault_id(&self.vault_id)?;
if self.nodes.len() > MAX_GRAPH_NODES || self.edges.len() > MAX_GRAPH_EDGES {
return Err(ValidationError::new(format!(
"a graph has at most {MAX_GRAPH_NODES} nodes and {MAX_GRAPH_EDGES} edges"
)));
}
for (i, node) in self.nodes.iter().enumerate() {
validate_text(&node.id, 700, "node id", false)?;
validate_text(&node.label, 300, "node label", false)?;
if i > 0 && self.nodes[i - 1].id >= node.id {
return Err(ValidationError::new(
"nodes must be sorted by id without repeats",
));
}
}
let known = |id: &str| {
self.nodes
.binary_search_by(|node| node.id.as_str().cmp(id))
.is_ok()
};
for (i, edge) in self.edges.iter().enumerate() {
if !known(&edge.from) || !known(&edge.to) {
return Err(ValidationError::new("an edge names a missing node"));
}
if !GRAPH_EDGE_TYPES.contains(&edge.kind.as_str())
&& !super::knowledge::RELATION_TYPES.contains(&edge.kind.as_str())
{
return Err(ValidationError::new("unknown edge type"));
}
if i > 0 {
let prev = &self.edges[i - 1];
if (&prev.from, &prev.to, &prev.kind) >= (&edge.from, &edge.to, &edge.kind) {
return Err(ValidationError::new("edges must be sorted without repeats"));
}
}
}
Ok(())
}
}