use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use super::manifest::ConfigProvenance;
pub const MAGIC_BYTES: &[u8; 13] = b"SQRY_GRAPH_V6";
pub const VERSION: u32 = 6;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphHeader {
pub version: u32,
pub node_count: usize,
pub edge_count: usize,
pub string_count: usize,
pub file_count: usize,
pub timestamp: u64,
#[serde(default)]
pub config_provenance: Option<ConfigProvenance>,
#[serde(default)]
pub plugin_versions: HashMap<String, String>,
}
impl GraphHeader {
#[must_use]
pub fn new(
node_count: usize,
edge_count: usize,
string_count: usize,
file_count: usize,
) -> Self {
Self {
version: VERSION,
node_count,
edge_count,
string_count,
file_count,
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
config_provenance: None,
plugin_versions: HashMap::new(),
}
}
#[must_use]
pub fn with_provenance(
node_count: usize,
edge_count: usize,
string_count: usize,
file_count: usize,
provenance: ConfigProvenance,
) -> Self {
Self {
version: VERSION,
node_count,
edge_count,
string_count,
file_count,
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
config_provenance: Some(provenance),
plugin_versions: HashMap::new(),
}
}
#[must_use]
pub fn with_provenance_and_plugins(
node_count: usize,
edge_count: usize,
string_count: usize,
file_count: usize,
provenance: ConfigProvenance,
plugin_versions: HashMap<String, String>,
) -> Self {
Self {
version: VERSION,
node_count,
edge_count,
string_count,
file_count,
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
config_provenance: Some(provenance),
plugin_versions,
}
}
#[must_use]
pub fn provenance(&self) -> Option<&ConfigProvenance> {
self.config_provenance.as_ref()
}
#[must_use]
pub fn has_provenance(&self) -> bool {
self.config_provenance.is_some()
}
#[must_use]
pub fn plugin_versions(&self) -> &HashMap<String, String> {
&self.plugin_versions
}
pub fn set_plugin_versions(&mut self, versions: HashMap<String, String>) {
self.plugin_versions = versions;
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
use std::path::PathBuf;
fn make_test_provenance() -> ConfigProvenance {
ConfigProvenance {
config_file: PathBuf::from(".sqry/graph/config/config.json"),
config_checksum: "abc123def456".to_string(),
schema_version: 1,
overrides: HashMap::new(),
build_timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
build_host: Some("test-host".to_string()),
}
}
#[test]
fn test_magic_bytes() {
assert_eq!(MAGIC_BYTES, b"SQRY_GRAPH_V6");
assert_eq!(MAGIC_BYTES.len(), 13);
}
#[test]
fn test_version() {
assert_eq!(VERSION, 6);
}
#[test]
fn test_graph_header_new() {
let header = GraphHeader::new(100, 50, 200, 10);
assert_eq!(header.version, VERSION);
assert_eq!(header.node_count, 100);
assert_eq!(header.edge_count, 50);
assert_eq!(header.string_count, 200);
assert_eq!(header.file_count, 10);
assert!(header.timestamp > 0);
assert!(header.config_provenance.is_none());
}
#[test]
fn test_graph_header_with_provenance() {
let provenance = make_test_provenance();
let header = GraphHeader::with_provenance(100, 50, 200, 10, provenance);
assert_eq!(header.version, VERSION);
assert_eq!(header.node_count, 100);
assert_eq!(header.edge_count, 50);
assert!(header.config_provenance.is_some());
assert_eq!(
header.config_provenance.as_ref().unwrap().config_checksum,
"abc123def456"
);
}
#[test]
fn test_graph_header_provenance_method() {
let header = GraphHeader::new(10, 5, 20, 2);
assert!(header.provenance().is_none());
let provenance = make_test_provenance();
let header_with = GraphHeader::with_provenance(10, 5, 20, 2, provenance);
assert!(header_with.provenance().is_some());
assert_eq!(
header_with.provenance().unwrap().config_checksum,
"abc123def456"
);
}
#[test]
fn test_graph_header_has_provenance() {
let header = GraphHeader::new(10, 5, 20, 2);
assert!(!header.has_provenance());
let provenance = make_test_provenance();
let header_with = GraphHeader::with_provenance(10, 5, 20, 2, provenance);
assert!(header_with.has_provenance());
}
#[test]
fn test_graph_header_clone() {
let header = GraphHeader::new(100, 50, 200, 10);
let cloned = header.clone();
assert_eq!(header.version, cloned.version);
assert_eq!(header.node_count, cloned.node_count);
assert_eq!(header.edge_count, cloned.edge_count);
assert_eq!(header.string_count, cloned.string_count);
assert_eq!(header.file_count, cloned.file_count);
}
#[test]
fn test_graph_header_debug() {
let header = GraphHeader::new(100, 50, 200, 10);
let debug_str = format!("{:?}", header);
assert!(debug_str.contains("GraphHeader"));
assert!(debug_str.contains("version"));
assert!(debug_str.contains("node_count"));
}
#[test]
fn test_graph_header_timestamp_is_recent() {
let header = GraphHeader::new(10, 5, 20, 2);
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
assert!(header.timestamp <= now);
assert!(header.timestamp >= now - 1);
}
#[test]
fn test_graph_header_zero_counts() {
let header = GraphHeader::new(0, 0, 0, 0);
assert_eq!(header.node_count, 0);
assert_eq!(header.edge_count, 0);
assert_eq!(header.string_count, 0);
assert_eq!(header.file_count, 0);
}
#[test]
fn test_graph_header_large_counts() {
let header = GraphHeader::new(1_000_000, 5_000_000, 10_000_000, 100_000);
assert_eq!(header.node_count, 1_000_000);
assert_eq!(header.edge_count, 5_000_000);
assert_eq!(header.string_count, 10_000_000);
assert_eq!(header.file_count, 100_000);
}
#[test]
fn test_graph_header_plugin_versions_empty_by_default() {
let header = GraphHeader::new(10, 5, 20, 2);
assert!(header.plugin_versions().is_empty());
}
#[test]
fn test_graph_header_set_plugin_versions() {
let mut header = GraphHeader::new(10, 5, 20, 2);
let mut versions = HashMap::new();
versions.insert("rust".to_string(), "3.3.0".to_string());
versions.insert("javascript".to_string(), "3.3.0".to_string());
header.set_plugin_versions(versions.clone());
assert_eq!(header.plugin_versions().len(), 2);
assert_eq!(
header.plugin_versions().get("rust"),
Some(&"3.3.0".to_string())
);
assert_eq!(
header.plugin_versions().get("javascript"),
Some(&"3.3.0".to_string())
);
}
#[test]
fn test_graph_header_with_provenance_and_plugins() {
let provenance = make_test_provenance();
let mut plugin_versions = HashMap::new();
plugin_versions.insert("rust".to_string(), "3.3.0".to_string());
plugin_versions.insert("python".to_string(), "3.3.0".to_string());
let header = GraphHeader::with_provenance_and_plugins(
100,
50,
200,
10,
provenance,
plugin_versions.clone(),
);
assert_eq!(header.version, VERSION);
assert_eq!(header.node_count, 100);
assert!(header.config_provenance.is_some());
assert_eq!(header.plugin_versions().len(), 2);
assert_eq!(
header.plugin_versions().get("rust"),
Some(&"3.3.0".to_string())
);
}
}