use std::collections::BTreeMap;
use std::fmt;
use serde::{Deserialize, Deserializer, Serialize};
use smallvec::SmallVec;
use crate::{
ByteStringType, CharacterStringType, CoreError, CoreResult, DbString, DecimalType,
ExtensionTypeId, LabelSet, PropertyValueType, RecordTypeId, Value,
};
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
#[repr(transparent)]
pub struct GraphTypeId(pub u64);
impl<'de> Deserialize<'de> for GraphTypeId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let raw = u64::deserialize(deserializer)?;
Self::new(raw).map_err(serde::de::Error::custom)
}
}
impl GraphTypeId {
pub const fn new(value: u64) -> CoreResult<Self> {
if value == 0 {
Err(CoreError::ZeroIdentifier)
} else {
Ok(Self(value))
}
}
#[must_use]
pub const fn get(self) -> u64 {
self.0
}
}
impl fmt::Display for GraphTypeId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "GraphTypeId({})", self.0)
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct GraphType {
pub id: GraphTypeId,
pub name: DbString,
pub node_types: BTreeMap<DbString, NodeTypeDef>,
pub edge_types: BTreeMap<DbString, EdgeTypeDef>,
pub record_types: BTreeMap<RecordTypeId, RecordTypeDef>,
pub key_label_set_policy: KeyLabelSetPolicy,
}
impl GraphType {
#[must_use]
pub fn new(id: GraphTypeId, name: DbString) -> Self {
Self {
id,
name,
node_types: BTreeMap::new(),
edge_types: BTreeMap::new(),
record_types: BTreeMap::new(),
key_label_set_policy: KeyLabelSetPolicy::default(),
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct NodeTypeDef {
pub labels: LabelSet,
pub properties: SmallVec<[PropertyDef; 8]>,
pub key: Option<NodeKey>,
#[serde(default)]
pub validation_mode: ValidationMode,
}
impl NodeTypeDef {
#[must_use]
pub fn new(labels: LabelSet) -> Self {
Self {
labels,
properties: SmallVec::new(),
key: None,
validation_mode: ValidationMode::Strict,
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct NodeTypeDefV1 {
pub labels: LabelSet,
pub properties: SmallVec<[PropertyDefV1; 8]>,
pub key: Option<NodeKey>,
}
impl NodeTypeDefV1 {
#[must_use]
pub fn new(labels: LabelSet) -> Self {
Self {
labels,
properties: SmallVec::new(),
key: None,
}
}
}
impl From<NodeTypeDefV1> for NodeTypeDef {
fn from(value: NodeTypeDefV1) -> Self {
Self {
labels: value.labels,
properties: value.properties.into_iter().map(Into::into).collect(),
key: value.key,
validation_mode: ValidationMode::Strict,
}
}
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct NodeKey {
pub property_names: SmallVec<[DbString; 2]>,
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum EdgeEndpointDef {
Any,
NodeType(NodeTypeRef),
OneOf(SmallVec<[NodeTypeRef; 4]>),
}
impl EdgeEndpointDef {
#[must_use]
pub fn one_of(refs: impl IntoIterator<Item = NodeTypeRef>) -> Self {
let mut buf: SmallVec<[NodeTypeRef; 4]> = refs.into_iter().collect();
buf.sort_unstable_by(|a, b| a.0.cmp(&b.0));
buf.dedup();
assert!(
!buf.is_empty(),
"EdgeEndpointDef::one_of called with empty NodeTypeRef set"
);
match buf.len() {
1 => Self::NodeType(buf[0].clone()),
_ => Self::OneOf(buf),
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct EdgeTypeDef {
pub label: DbString,
pub source_node_type: EdgeEndpointDef,
pub target_node_type: EdgeEndpointDef,
pub properties: SmallVec<[PropertyDef; 4]>,
#[serde(default)]
pub validation_mode: ValidationMode,
}
impl EdgeTypeDef {
#[must_use]
pub fn new(label: DbString, source: NodeTypeRef, target: NodeTypeRef) -> Self {
Self::new_with_endpoints(
label,
EdgeEndpointDef::NodeType(source),
EdgeEndpointDef::NodeType(target),
)
}
#[must_use]
pub fn new_with_endpoints(
label: DbString,
source: EdgeEndpointDef,
target: EdgeEndpointDef,
) -> Self {
Self {
label,
source_node_type: source,
target_node_type: target,
properties: SmallVec::new(),
validation_mode: ValidationMode::Strict,
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct EdgeTypeDefV1 {
pub label: DbString,
pub source_node_type: NodeTypeRef,
pub target_node_type: NodeTypeRef,
pub properties: SmallVec<[PropertyDefV1; 4]>,
}
impl EdgeTypeDefV1 {
#[must_use]
pub fn new(label: DbString, source: NodeTypeRef, target: NodeTypeRef) -> Self {
Self {
label,
source_node_type: source,
target_node_type: target,
properties: SmallVec::new(),
}
}
}
impl From<EdgeTypeDefV1> for EdgeTypeDef {
fn from(value: EdgeTypeDefV1) -> Self {
Self {
label: value.label,
source_node_type: EdgeEndpointDef::NodeType(value.source_node_type),
target_node_type: EdgeEndpointDef::NodeType(value.target_node_type),
properties: value.properties.into_iter().map(Into::into).collect(),
validation_mode: ValidationMode::Strict,
}
}
}
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum ValidationMode {
#[default]
Strict,
Warn,
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[repr(transparent)]
pub struct NodeTypeRef(pub DbString);
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[repr(transparent)]
pub struct RecordTypeRef(pub RecordTypeId);
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum RecordFieldStructure {
Open,
Closed(Vec<RecordFieldStructureDef>),
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct RecordFieldStructureDef {
pub name: DbString,
pub field_type: RecordFieldStructureType,
pub required: bool,
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum RecordFieldStructureType {
Scalar(PropertyValueType),
CharacterString(CharacterStringType),
Decimal(DecimalType),
ByteString(ByteStringType),
List(Box<RecordFieldStructureType>),
Record(Box<RecordFieldStructure>),
NotNull(Box<RecordFieldStructureType>),
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct PropertyDef {
pub name: DbString,
pub value_type: ValueType,
pub nullable: bool,
pub default: Option<Value>,
#[serde(default)]
pub immutable: bool,
#[serde(default)]
pub unique: bool,
#[serde(default)]
pub record_fields: Option<Box<RecordFieldStructure>>,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct PropertyDefV1 {
pub name: DbString,
pub value_type: ValueType,
pub nullable: bool,
pub default: Option<Value>,
}
impl From<PropertyDefV1> for PropertyDef {
fn from(value: PropertyDefV1) -> Self {
Self {
name: value.name,
value_type: value.value_type,
nullable: value.nullable,
default: value.default,
immutable: false,
unique: false,
record_fields: None,
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct ValueType {
pub predefined: Option<PredefinedValueType>,
#[serde(default)]
pub decimal_type: Option<DecimalType>,
#[serde(default)]
pub character_string_type: Option<CharacterStringType>,
#[serde(default)]
pub byte_string_type: Option<ByteStringType>,
pub union: Option<Vec<ValueType>>,
pub list_of: Option<Box<ValueType>>,
pub record: Option<RecordTypeRef>,
pub not_null: bool,
pub cardinality: ValueTypeCardinality,
}
impl ValueType {
#[must_use]
pub const fn predefined(predefined: PredefinedValueType) -> Self {
Self {
predefined: Some(predefined),
decimal_type: None,
character_string_type: None,
byte_string_type: None,
union: None,
list_of: None,
record: None,
not_null: false,
cardinality: ValueTypeCardinality::ExactlyOne,
}
}
#[must_use]
pub fn list_of(item: Self) -> Self {
Self {
predefined: None,
decimal_type: None,
character_string_type: None,
byte_string_type: None,
union: None,
list_of: Some(Box::new(item)),
record: None,
not_null: false,
cardinality: ValueTypeCardinality::ExactlyOne,
}
}
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum PredefinedValueType {
Bool,
Int,
Int8,
Int16,
Int32,
Int64,
Int128,
Uint,
Uint8,
Uint16,
Uint32,
Uint64,
Uint128,
Float,
Float32,
Float64,
Decimal,
String,
Bytes,
Date,
LocalTime,
ZonedTime,
LocalDateTime,
ZonedDateTime,
Duration,
DurationYearToMonth,
DurationDayToSecond,
NodeRef,
EdgeRef,
GraphRef,
TableRef,
Path,
Uuid,
Extended(ExtensionTypeId),
Vector,
Json,
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum ValueTypeCardinality {
ExactlyOne,
ZeroOrOne,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct RecordTypeDef {
pub id: RecordTypeId,
pub name: DbString,
pub fields: SmallVec<[PropertyDef; 4]>,
}
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum KeyLabelSetPolicy {
NoOverlap,
#[default]
Containment,
}
#[cfg(test)]
#[path = "schema_tests.rs"]
mod tests;