1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
//! Owned runtime descriptors for dynamic ORM access.
//!
//! The static Rust traits remain the typed API. These descriptors mirror the
//! same schema metadata using owned data so language bindings and generated
//! schemas can register types without borrowing from Rust model structs.
use std::sync::Arc;
use serde::{Deserialize, Serialize};
use crate::_attribute::ValueType;
use crate::_entity::Annotation;
/// Owned metadata about one attribute owned by an entity or relation type.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct OwnedAttributeDescriptor {
/// Binding-facing field name, stable across language facades.
pub field_name: String,
/// TypeDB attribute type name.
pub attr_name: String,
/// TypeDB value type.
pub value_type: ValueType,
/// Ownership annotations such as `@key`, `@unique`, `@card`, or `@distinct`.
///
/// `@distinct` appears here as `Annotation::Distinct` when the ownership is
/// declared as an ordered list with the distinct constraint.
pub annotations: Vec<Annotation>,
/// Whether the field may be omitted from dynamic input and hydration.
pub is_optional: bool,
/// Whether this ownership is declared as an ordered list (`owns name[]`).
///
/// Projected construction, write lowering, and hydration retain caller/provider
/// order. The `@distinct` annotation is valid only when this is `true`.
#[serde(default)]
pub is_ordered: bool,
/// Optional `@doc("...")` documentation annotation on the ownership (TypeDB 3.12+).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub doc: Option<String>,
/// `@meta("key", "value")` annotations on the ownership, keyed by meta key (TypeDB 3.12+).
#[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")]
pub meta: std::collections::BTreeMap<String, String>,
}
impl OwnedAttributeDescriptor {
/// Whether this attribute has a `@key` annotation.
pub fn is_key(&self) -> bool {
self.annotations
.iter()
.any(|annotation| matches!(annotation, Annotation::Key))
}
/// Whether this attribute has a `@unique` annotation.
pub fn is_unique(&self) -> bool {
self.annotations
.iter()
.any(|annotation| matches!(annotation, Annotation::Unique))
}
/// Return the cardinality annotation, if present.
pub fn cardinality(&self) -> Option<(u32, Option<u32>)> {
self.annotations
.iter()
.find_map(|annotation| match annotation {
Annotation::Card(min, max) => Some((*min, *max)),
_ => None,
})
}
}
/// Owned metadata about one role in a relation type.
///
/// The `overrides` field is present when this role specializes a parent
/// relation's role via TypeDB's `relates child as parent` syntax. Own
/// specializing roles carry `overrides: Some(<parent role name>)`; plain-
/// inherited role entries copied into a subtype's effective set keep the
/// parent role's markers, including any `overrides` the parent itself carried.
///
/// `is_abstract` mirrors the role definition's schema-level `@abstract` marker.
/// Abstractness gates direct play only at the declaring relation's own scope:
/// the engine rejects players for the role on instances of the declaring type,
/// while a concrete sub-relation that plain-inherits the role can play it.
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct RoleDescriptor {
/// Role name within the relation.
pub role_name: String,
/// Entity type names allowed to play this role.
pub player_type_names: Vec<String>,
/// Optional role cardinality, where `None` max means unbounded.
pub cardinality: Option<(u32, Option<u32>)>,
/// Parent role name this role specializes, or `null` for plain roles.
pub overrides: Option<String>,
/// Whether this role carries a schema-level `@abstract` annotation.
#[serde(default)]
pub is_abstract: bool,
/// Whether this role is declared as an ordered list (`relates name[]`).
///
/// Projected construction, write lowering, and hydration retain caller/provider
/// order.
#[serde(default)]
pub ordered: bool,
/// Whether this role carries a schema-level `@distinct` annotation.
///
/// Valid only when `ordered` is `true`.
#[serde(default)]
pub distinct: bool,
/// Relation-side authoring of the player's `plays` cardinality for this role.
///
/// Consumed by `SchemaInfo::from_descriptors` to build the per-player
/// `plays_cardinalities` overlay (keyed `"{relation_type_name}:{role_name}"`).
/// Distinct from `cardinality`, which is the relates-side cardinality
/// constraining how many players may fill the role per relation instance.
#[serde(default)]
pub plays_cardinality: Option<(u32, Option<u32>)>,
/// Optional `@doc("...")` documentation annotation on the role (TypeDB 3.12+).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub doc: Option<String>,
/// `@meta("key", "value")` annotations on the role, keyed by meta key (TypeDB 3.12+).
#[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")]
pub meta: std::collections::BTreeMap<String, String>,
}
/// Runtime descriptor for an entity type.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct EntityDescriptor {
/// TypeDB entity type name.
pub type_name: String,
/// Whether this entity type is abstract.
pub is_abstract: bool,
/// Optional parent type name.
pub parent_type: Option<String>,
/// Attributes owned by this entity.
pub owned_attributes: Vec<OwnedAttributeDescriptor>,
/// Optional `@doc("...")` documentation annotation on the type (TypeDB 3.12+).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub doc: Option<String>,
/// `@meta("key", "value")` annotations on the type, keyed by meta key (TypeDB 3.12+).
#[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")]
pub meta: std::collections::BTreeMap<String, String>,
}
impl EntityDescriptor {
/// Return the first key attribute, if any.
pub fn key_attribute(&self) -> Option<&OwnedAttributeDescriptor> {
self.owned_attributes.iter().find(|attr| attr.is_key())
}
/// Lookup an attribute by field name or TypeDB attribute name.
pub fn attribute(&self, name: &str) -> Option<&OwnedAttributeDescriptor> {
self.owned_attributes
.iter()
.find(|attr| attr.field_name == name || attr.attr_name == name)
}
}
/// Runtime descriptor for a relation type.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RelationDescriptor {
/// TypeDB relation type name.
pub type_name: String,
/// Whether this relation type is abstract.
pub is_abstract: bool,
/// Optional parent type name.
pub parent_type: Option<String>,
/// Attributes owned by this relation.
pub owned_attributes: Vec<OwnedAttributeDescriptor>,
/// The relation's *effective* role set: plain-inherited parent roles
/// (parent declaration order first), then own and specializing roles —
/// excluding any parent role overridden via `relates child as parent`.
///
/// The registry stores descriptors as-is and never resolves inheritance,
/// so runtime consumers (query building, hydration) see exactly this
/// list; it must therefore be the set of roles the engine accepts on
/// instances of this relation type. TypeDB rejects a player for an
/// overridden parent role on a subtype instance, which is why overridden
/// roles are excluded while plain-inherited ones are flattened in —
/// mirroring how `owned_attributes` flattens inherited attributes.
pub roles: Vec<RoleDescriptor>,
/// Optional `@doc("...")` documentation annotation on the type (TypeDB 3.12+).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub doc: Option<String>,
/// `@meta("key", "value")` annotations on the type, keyed by meta key (TypeDB 3.12+).
#[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")]
pub meta: std::collections::BTreeMap<String, String>,
}
impl RelationDescriptor {
/// Return the first key attribute, if any.
pub fn key_attribute(&self) -> Option<&OwnedAttributeDescriptor> {
self.owned_attributes.iter().find(|attr| attr.is_key())
}
/// Lookup an attribute by field name or TypeDB attribute name.
pub fn attribute(&self, name: &str) -> Option<&OwnedAttributeDescriptor> {
self.owned_attributes
.iter()
.find(|attr| attr.field_name == name || attr.attr_name == name)
}
/// Lookup a role by role name.
pub fn role(&self, name: &str) -> Option<&RoleDescriptor> {
self.roles.iter().find(|role| role.role_name == name)
}
}
/// Owned descriptor for any registered TypeDB object type.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "kind", content = "descriptor", rename_all = "snake_case")]
pub enum TypeDescriptor {
/// Entity type descriptor.
Entity(EntityDescriptor),
/// Relation type descriptor.
Relation(RelationDescriptor),
}
impl TypeDescriptor {
/// Return the TypeDB type name for this descriptor.
pub fn type_name(&self) -> &str {
match self {
Self::Entity(descriptor) => &descriptor.type_name,
Self::Relation(descriptor) => &descriptor.type_name,
}
}
}
/// Shared reference to any registered descriptor.
#[derive(Debug, Clone)]
pub enum TypeDescriptorRef {
/// Registered entity descriptor.
Entity(Arc<EntityDescriptor>),
/// Registered relation descriptor.
Relation(Arc<RelationDescriptor>),
}
impl TypeDescriptorRef {
/// Return the TypeDB type name for this descriptor reference.
pub fn type_name(&self) -> &str {
match self {
Self::Entity(descriptor) => &descriptor.type_name,
Self::Relation(descriptor) => &descriptor.type_name,
}
}
/// Clone this reference into an owned descriptor snapshot.
pub fn to_owned_descriptor(&self) -> TypeDescriptor {
match self {
Self::Entity(descriptor) => TypeDescriptor::Entity((**descriptor).clone()),
Self::Relation(descriptor) => TypeDescriptor::Relation((**descriptor).clone()),
}
}
}