#![forbid(unsafe_code)]
pub mod binder;
pub use binder::{BindError, BindErrorKind, Binder};
pub mod catalog;
pub use catalog::{RuntimeCatalog, RuntimePropId, RuntimeTypeId, runtime_relation_type_id};
pub mod expr;
pub use expr::{BinaryOpKind, CaseArm, ExprArena, IrExpr, IrLiteral, UnaryOpKind};
pub use graphforge_ast::QuantifierKind;
pub mod plan;
pub use plan::{GraphOp, GraphPlan, GraphPlanBuilder, OntologyMode, SortKey};
pub mod procedure;
pub use procedure::{ProcedureDefinition, ProcedureField, ProcedureRegistry, ProcedureYield};
use std::fmt;
use std::str::FromStr;
use serde::{Deserialize, Serialize};
pub use graphforge_core::{GfError, PropId, TypeId};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct VarId(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ExprId(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct IrVersion {
pub major: u16,
pub minor: u16,
pub patch: u16,
}
impl IrVersion {
pub const CURRENT: Self = Self {
major: 0,
minor: 3,
patch: 0,
};
}
impl fmt::Display for IrVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
}
}
impl From<&str> for IrVersion {
fn from(s: &str) -> Self {
let parts: Vec<&str> = s.splitn(3, '.').collect();
let parse = |p: &&str| p.parse::<u16>().unwrap_or(0);
Self {
major: parts.first().map_or(0, parse),
minor: parts.get(1).map_or(0, parse),
patch: parts.get(2).map_or(0, parse),
}
}
}
impl FromStr for IrVersion {
type Err = std::convert::Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self::from(s))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct OntologyVersion(pub String);
impl fmt::Display for OntologyVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl<S: Into<String>> From<S> for OntologyVersion {
fn from(s: S) -> Self {
Self(s.into())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Direction {
Out,
In,
Undirected,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum SortOrder {
Asc,
Desc,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ProjectItem {
pub expr: ExprId,
pub alias: Option<String>,
#[serde(default)]
pub out_var: Option<VarId>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AggExpr {
pub func: AggFunc,
pub arg: Option<ExprId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub percentile: Option<ExprId>,
pub alias: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub out_var: Option<VarId>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum AggFunc {
Count,
CountDistinct,
Sum,
SumDistinct,
Avg,
AvgDistinct,
Min,
Max,
Collect,
CollectDistinct,
PercentileDisc,
PercentileCont,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CreateNodeSpec {
pub var: VarId,
pub labels: Vec<TypeId>,
pub properties: Option<ExprId>,
#[serde(default)]
pub is_reference: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CreateEdgeSpec {
pub var: VarId,
pub src: VarId,
pub dst: VarId,
pub rel_type: Option<TypeId>,
pub direction: Direction,
pub properties: Option<ExprId>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct CreatePattern {
#[serde(default)]
pub nodes: Vec<CreateNodeSpec>,
#[serde(default)]
pub edges: Vec<CreateEdgeSpec>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SetPropItem {
pub target: VarId,
pub prop: PropId,
pub prop_name: String,
pub value: ExprId,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SetMapItem {
pub target: VarId,
pub map: ExprId,
pub replace: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LabelItem {
pub target: VarId,
pub labels: Vec<TypeId>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum MergeSetItem {
Property(SetPropItem),
Map(SetMapItem),
AddLabels {
target: VarId,
labels: Vec<TypeId>,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RemovePropItem {
pub target: VarId,
pub prop: PropId,
pub prop_name: String,
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use super::*;
#[test]
fn ir_version_display() {
let v = IrVersion {
major: 1,
minor: 2,
patch: 3,
};
assert_eq!(v.to_string(), "1.2.3");
}
#[test]
fn ir_version_from_str() {
let v = IrVersion::from("2.0.1");
assert_eq!(v.major, 2);
assert_eq!(v.minor, 0);
assert_eq!(v.patch, 1);
}
#[test]
fn ir_version_from_str_partial() {
let v = IrVersion::from("3.1");
assert_eq!(v.major, 3);
assert_eq!(v.minor, 1);
assert_eq!(v.patch, 0);
}
#[test]
fn ir_version_from_str_invalid_falls_back() {
let v = IrVersion::from("not.a.version");
assert_eq!(
v,
IrVersion {
major: 0,
minor: 0,
patch: 0
}
);
}
#[test]
fn newtype_hash_keys() {
let mut map: HashMap<VarId, &str> = HashMap::new();
map.insert(VarId(0), "a");
map.insert(VarId(1), "b");
assert_eq!(map[&VarId(0)], "a");
let mut pmap: HashMap<PropId, u32> = HashMap::new();
pmap.insert(PropId(5), 42);
assert_eq!(pmap[&PropId(5)], 42);
let mut emap: HashMap<ExprId, bool> = HashMap::new();
emap.insert(ExprId(10), true);
assert!(emap[&ExprId(10)]);
}
#[test]
fn serde_roundtrip_ir_version() {
let v = IrVersion {
major: 1,
minor: 2,
patch: 3,
};
let json = serde_json::to_string(&v).unwrap();
let back: IrVersion = serde_json::from_str(&json).unwrap();
assert_eq!(v, back);
}
#[test]
fn serde_roundtrip_ontology_version() {
let v = OntologyVersion::from("abc123checksum");
let json = serde_json::to_string(&v).unwrap();
let back: OntologyVersion = serde_json::from_str(&json).unwrap();
assert_eq!(v, back);
}
#[test]
fn serde_roundtrip_direction() {
for d in [Direction::Out, Direction::In, Direction::Undirected] {
let json = serde_json::to_string(&d).unwrap();
let back: Direction = serde_json::from_str(&json).unwrap();
assert_eq!(d, back);
}
}
#[test]
fn serde_roundtrip_sort_order() {
for s in [SortOrder::Asc, SortOrder::Desc] {
let json = serde_json::to_string(&s).unwrap();
let back: SortOrder = serde_json::from_str(&json).unwrap();
assert_eq!(s, back);
}
}
#[test]
fn type_id_reexported() {
let id = TypeId(42);
assert_eq!(id.0, 42);
}
#[test]
fn create_pattern_roundtrip() {
let p = CreatePattern {
nodes: vec![CreateNodeSpec {
var: VarId(0),
labels: vec![TypeId(3)],
properties: Some(ExprId(1)),
is_reference: false,
}],
edges: vec![CreateEdgeSpec {
var: VarId(1),
src: VarId(0),
dst: VarId(2),
rel_type: Some(TypeId(4)),
direction: Direction::Out,
properties: None,
}],
};
let json = serde_json::to_string(&p).unwrap();
let back: CreatePattern = serde_json::from_str(&json).unwrap();
assert_eq!(p, back);
}
#[test]
fn create_pattern_default_is_empty() {
let p = CreatePattern::default();
assert!(p.nodes.is_empty() && p.edges.is_empty());
}
}