use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use crate::{
error::{GraphArError, Result},
metadata::property::PropertyGroup,
types::{AdjListType, FileType},
};
pub const DEFAULT_EDGE_CHUNK_SIZE: u64 = 4_194_304;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdjListInfo {
pub ordered: bool,
pub aligned_by: String,
pub file_type: FileType,
#[serde(skip_serializing_if = "Option::is_none")]
pub prefix: Option<String>,
}
impl AdjListInfo {
pub fn adj_list_type(&self) -> Result<AdjListType> {
AdjListType::try_from((
if self.ordered { "true" } else { "false" },
self.aligned_by.as_str(),
))
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EdgeInfo {
pub src_type: String,
pub edge_type: String,
pub dst_type: String,
#[serde(default = "default_edge_chunk_size")]
pub chunk_size: u64,
pub src_chunk_size: u64,
pub dst_chunk_size: u64,
#[serde(default)]
pub directed: bool,
pub prefix: String,
#[serde(default)]
pub adj_lists: Vec<AdjListInfo>,
#[serde(default)]
pub property_groups: Vec<PropertyGroup>,
#[serde(default = "super::vertex_info::default_version")]
pub version: String,
}
fn default_edge_chunk_size() -> u64 {
DEFAULT_EDGE_CHUNK_SIZE
}
impl EdgeInfo {
pub fn new(
src_type: impl Into<String>,
edge_type: impl Into<String>,
dst_type: impl Into<String>,
chunk_size: u64,
src_chunk_size: u64,
dst_chunk_size: u64,
directed: bool,
) -> Self {
let (src, edge, dst) = (src_type.into(), edge_type.into(), dst_type.into());
let prefix = format!("edge/{}_{}_{}/", src, edge, dst);
Self {
src_type: src,
edge_type: edge,
dst_type: dst,
chunk_size,
src_chunk_size,
dst_chunk_size,
directed,
prefix,
adj_lists: Vec::new(),
property_groups: Vec::new(),
version: super::vertex_info::GAR_VERSION.to_string(),
}
}
pub fn with_adj_list(mut self, info: AdjListInfo) -> Self {
self.adj_lists.push(info);
self
}
pub fn with_property_group(mut self, group: PropertyGroup) -> Self {
self.property_groups.push(group);
self
}
pub fn vertex_chunk_count(&self, adj_type: &AdjListType) -> u64 {
let vchunk_size = if adj_type.aligned_by_src() {
self.src_chunk_size
} else {
self.dst_chunk_size
};
vchunk_size
}
pub fn get_adj_list_info(&self, adj_type: &AdjListType) -> Option<&AdjListInfo> {
self.adj_lists
.iter()
.find(|a| a.adj_list_type().ok().as_ref() == Some(adj_type))
}
pub fn adj_list_chunk_path(
&self,
adj_type: &AdjListType,
vertex_chunk: u64,
edge_chunk: u64,
) -> Result<PathBuf> {
let info = self.get_adj_list_info(adj_type).ok_or_else(|| {
GraphArError::AdjListTypeNotConfigured(adj_type.dir_name().to_string())
})?;
Ok(PathBuf::from(&self.prefix)
.join(adj_type.dir_name())
.join("adj_list")
.join(format!("part{vertex_chunk}"))
.join(format!(
"chunk{}.{}",
edge_chunk,
info.file_type.extension()
)))
}
pub fn offset_chunk_path(&self, adj_type: &AdjListType, vertex_chunk: u64) -> Result<PathBuf> {
let info = self.get_adj_list_info(adj_type).ok_or_else(|| {
GraphArError::AdjListTypeNotConfigured(adj_type.dir_name().to_string())
})?;
if !adj_type.is_ordered() {
return Err(GraphArError::Other(
"offset files only exist for ordered adjacency lists".into(),
));
}
Ok(PathBuf::from(&self.prefix)
.join(adj_type.dir_name())
.join("offset")
.join(format!(
"chunk{}.{}",
vertex_chunk,
info.file_type.extension()
)))
}
pub fn property_chunk_path(
&self,
adj_type: &AdjListType,
group: &PropertyGroup,
vertex_chunk: u64,
edge_chunk: u64,
) -> PathBuf {
PathBuf::from(&self.prefix)
.join(adj_type.dir_name())
.join(group.dir_name())
.join(format!("part{vertex_chunk}"))
.join(format!(
"chunk{}.{}",
edge_chunk,
group.file_type.extension()
))
}
pub fn find_property_group(&self, property_name: &str) -> Option<&PropertyGroup> {
self.property_groups
.iter()
.find(|g| g.has_property(property_name))
}
pub fn load(path: impl AsRef<std::path::Path>) -> Result<Self> {
let content = std::fs::read_to_string(path)?;
Ok(serde_yaml::from_str(&content)?)
}
pub fn save(&self, path: impl AsRef<std::path::Path>) -> Result<()> {
let content = serde_yaml::to_string(self)?;
std::fs::write(path, content)?;
Ok(())
}
}