1mod alias;
2pub mod describe;
3mod element;
4mod group;
5mod lane;
6pub mod witness;
7
8use super::{
9 ArgumentDescriptor, ArgumentPattern, ArityDescriptorTemplate, ArityPattern, CapabilityRegistry,
10 EmissionSpec, OperandDescriptor, OperandDescriptorTemplate, OutArityTable, StatePattern,
11};
12#[cfg(feature = "dynamic")]
13use crate::dynamic::DynApplier;
14use crate::{
15 IndexDomain,
16 element::{Arity, ElementShape},
17 index::GroupKey,
18 operands::{GroupOperand, Operand, OperandHandle},
19 operations::operation_manifests,
20};
21pub(crate) use alias::{
22 manifest_entry_alias, manifest_entry_aliases, manifest_entry_argument_pattern,
23 manifest_entry_set_argument_pattern, manifest_witness_alias, manifest_witness_argument_alias,
24 manifest_witness_set_argument_alias,
25};
26use describe::{DescribeArity, DescribeIndex, DescribeOperand, DescribeShape};
27pub(crate) use element::{
28 operation_element_entry, operation_element_method, operation_element_witness,
29};
30pub(crate) use group::{operation_group_entry, operation_group_witness};
31pub(crate) use lane::{operation_lane_entry, operation_lane_witness};
32pub use witness::{
33 AbsoluteCapability, AddCapability, ArgumentWitness, ArityWitness, BareValueCapability,
34 CastBoolCapability, CastDateTimeCapability, CastDurationCapability, CastFloatCapability,
35 CastIntCapability, CastStringCapability, CeilCapability, ClipCapability, CubeRootCapability,
36 DivideCapability, ElementShapeWitness, EntityAttributesWitness, EntityWitness,
37 EnumerableArityWitness, EqualityCapability, EquivalenceCapability, ExponentialCapability,
38 FloorCapability, GroupKeyWitness, GroupMemberWitness, GroupingCapability, IndexWitness,
39 IndicesInGroupWitness, IntCapability, KindTestCapability, LogarithmCapability,
40 MedianCapability, ModeCapability, ModuloCapability, MultiplyCapability, NegateCapability,
41 OrderingCapability, PowerCapability, RoundCapability, ScalarCapability,
42 ScalarKindTestCapability, SetSourceWitness, SignCapability, SortableCapability,
43 SortableIndexWitness, SquareRootCapability, StringCapability, SubtractCapability,
44 ValueDomainCapability, ValueDomainOnly, ValueWitness,
45};
46pub(crate) use witness::{operation_value_capability_marker, operation_value_capability_witness};
47
48const ELEMENT_INPUT_ARITY: usize = 0;
49
50pub struct OperationRegistry {
51 capabilities: CapabilityRegistry,
52 arities: OutArityTable,
53 manifests: Vec<OperationManifest>,
54}
55
56impl OperationRegistry {
57 #[must_use]
58 pub fn builtins() -> Self {
59 Self {
60 capabilities: CapabilityRegistry::builtins(),
61 arities: OutArityTable::builtins(),
62 manifests: operation_manifests(),
63 }
64 }
65
66 #[must_use]
67 pub fn resolve(&self, method: &str, input: &OperandDescriptor) -> Option<OperandDescriptor> {
68 self.resolve_with_arguments(method, input, &[])
69 }
70
71 #[must_use]
72 pub fn resolve_with_arguments(
73 &self,
74 method: &str,
75 input: &OperandDescriptor,
76 arguments: &[ArgumentDescriptor],
77 ) -> Option<OperandDescriptor> {
78 self.resolve_entry(method, input, arguments)
79 .map(|resolved| resolved.0)
80 }
81
82 #[cfg(feature = "dynamic")]
83 pub(crate) fn resolve_dispatch(
84 &self,
85 method: &str,
86 input: &OperandDescriptor,
87 arguments: &[ArgumentDescriptor],
88 ) -> Option<(OperandDescriptor, DynApplier)> {
89 self.resolve_entry(method, input, arguments)
90 .map(|(output, entry)| (output, entry.applier))
91 }
92
93 fn resolve_entry(
94 &self,
95 method: &str,
96 input: &OperandDescriptor,
97 arguments: &[ArgumentDescriptor],
98 ) -> Option<(OperandDescriptor, &OperationManifestEntry)> {
99 if let Some(resolved) = self.resolve_here(method, input, arguments) {
100 return Some(resolved);
101 }
102
103 let OperandDescriptor::Group {
104 member,
105 key,
106 payload,
107 } = input
108 else {
109 return None;
110 };
111 let (payload, entry) = self.resolve_entry(method, payload, arguments)?;
112 let output = OperandDescriptor::Group {
113 member: member.clone(),
114 key: key.clone(),
115 payload: Box::new(payload),
116 };
117
118 Some((output, entry))
119 }
120
121 fn resolve_here(
122 &self,
123 method: &str,
124 input: &OperandDescriptor,
125 arguments: &[ArgumentDescriptor],
126 ) -> Option<(OperandDescriptor, &OperationManifestEntry)> {
127 self.manifests
128 .iter()
129 .filter(|manifest| manifest.method() == method)
130 .find_map(|manifest| {
131 manifest.resolve(&self.capabilities, &self.arities, input, arguments)
132 })
133 }
134
135 pub fn method_names(&self) -> impl Iterator<Item = &'static str> + '_ {
136 self.manifests.iter().map(OperationManifest::method)
137 }
138}
139
140pub struct OperationManifest {
141 method: &'static str,
142 entries: Vec<OperationManifestEntry>,
143}
144
145impl OperationManifest {
146 pub const fn new(method: &'static str, entries: Vec<OperationManifestEntry>) -> Self {
147 Self { method, entries }
148 }
149
150 pub const fn method(&self) -> &'static str {
151 self.method
152 }
153
154 fn resolve(
155 &self,
156 capabilities: &CapabilityRegistry,
157 arities: &OutArityTable,
158 input: &OperandDescriptor,
159 arguments: &[ArgumentDescriptor],
160 ) -> Option<(OperandDescriptor, &OperationManifestEntry)> {
161 self.entries.iter().find_map(|entry| {
162 entry
163 .resolve(capabilities, arities, input, arguments)
164 .map(|output| (output, entry))
165 })
166 }
167}
168
169pub struct OperationManifestEntry {
170 input: StatePattern,
171 arguments: Vec<ArgumentPattern>,
172 output: OperandDescriptorTemplate,
173 #[cfg(feature = "dynamic")]
174 applier: DynApplier,
175}
176
177impl OperationManifestEntry {
178 pub const fn new(
179 input: StatePattern,
180 arguments: Vec<ArgumentPattern>,
181 output: OperandDescriptorTemplate,
182 #[cfg(feature = "dynamic")] applier: DynApplier,
183 ) -> Self {
184 Self {
185 input,
186 arguments,
187 output,
188 #[cfg(feature = "dynamic")]
189 applier,
190 }
191 }
192
193 #[must_use]
194 pub fn element<S, T>(
195 arguments: Vec<ArgumentPattern>,
196 emission: EmissionSpec,
197 #[cfg(feature = "dynamic")] applier: DynApplier,
198 ) -> Self
199 where
200 S: DescribeShape + ElementShape,
201 T: DescribeShape + ElementShape,
202 {
203 Self::new(
204 StatePattern::Lane {
205 shape: S::shape_pattern(),
206 arity: ArityPattern::Variable(ELEMENT_INPUT_ARITY, Box::new(ArityPattern::Any)),
207 },
208 arguments,
209 OperandDescriptorTemplate::Lane {
210 shape: T::shape_template(),
211 arity: ArityDescriptorTemplate::EmissionOf {
212 input: ELEMENT_INPUT_ARITY,
213 emission,
214 },
215 },
216 #[cfg(feature = "dynamic")]
217 applier,
218 )
219 }
220
221 #[must_use]
222 pub fn lane<S, C, T>(
223 arguments: Vec<ArgumentPattern>,
224 #[cfg(feature = "dynamic")] applier: DynApplier,
225 ) -> Self
226 where
227 S: DescribeShape + ElementShape,
228 C: DescribeArity + Arity,
229 T: DescribeOperand,
230 {
231 Self::new(
232 <OperandHandle<S, C> as DescribeOperand>::state_pattern(),
233 arguments,
234 T::operand_template(),
235 #[cfg(feature = "dynamic")]
236 applier,
237 )
238 }
239
240 #[must_use]
241 pub fn group<M, K, P, T>(
242 arguments: Vec<ArgumentPattern>,
243 #[cfg(feature = "dynamic")] applier: DynApplier,
244 ) -> Self
245 where
246 M: DescribeIndex + IndexDomain,
247 K: DescribeIndex + GroupKey,
248 P: DescribeOperand + Operand,
249 T: DescribeOperand,
250 {
251 Self::new(
252 <GroupOperand<M, K, P> as DescribeOperand>::state_pattern(),
253 arguments,
254 T::operand_template(),
255 #[cfg(feature = "dynamic")]
256 applier,
257 )
258 }
259
260 fn resolve(
261 &self,
262 capabilities: &CapabilityRegistry,
263 arities: &OutArityTable,
264 input: &OperandDescriptor,
265 arguments: &[ArgumentDescriptor],
266 ) -> Option<OperandDescriptor> {
267 if arguments.len() != self.arguments.len() {
268 return None;
269 }
270
271 let mut bindings = self.input.matches(input, capabilities)?;
272
273 self.arguments
274 .iter()
275 .zip(arguments)
276 .all(|(pattern, argument)| pattern.matches(argument, capabilities, &mut bindings))
277 .then(|| self.output.fill(&bindings, capabilities, arities))
278 }
279}
280
281macro_rules! operation_manifest_name {
282 ($registry_name:literal $method:ident) => {
283 $registry_name
284 };
285 ($method:ident) => {
286 stringify!($method)
287 };
288}
289
290macro_rules! operation_policy_method {
291 (
292 OnError,
293 $method:ident,
294 policy[$policy:path $(= $($constructor:tt)+)?],
295 $receiver:ty
296 ) => {
297 const fn verify_method<O: OnError>()
298 where
299 $policy: $crate::operations::ErrorPolicy<O>,
300 {
301 let _ = O::$method::<$policy>;
302 }
303
304 verify_method::<$receiver>();
305 };
306 (
307 OnBucketError,
308 $method:ident,
309 policy[$policy:path $(= $($constructor:tt)+)?],
310 $receiver:ty
311 ) => {
312 const fn verify_method<O: OnBucketError>()
313 where
314 $policy: $crate::operations::BucketErrorPolicy<O>,
315 {
316 let _ = O::$method::<$policy>;
317 }
318
319 verify_method::<$receiver>();
320 };
321 (
322 OnKeyError,
323 $method:ident,
324 policy[$policy:path $(= $($constructor:tt)+)?],
325 $receiver:ty
326 ) => {
327 const fn verify_method<O: OnKeyError>()
328 where
329 $policy: $crate::operations::KeyErrorPolicy<O>,
330 {
331 let _ = O::$method::<$policy>;
332 }
333
334 verify_method::<$receiver>();
335 };
336 (
337 $trait:ident $(<$($trait_argument:ty),+ $(,)?>)?,
338 $method:ident,
339 policy[],
340 $receiver:ty
341 ) => {
342 const fn verify_method<O: $trait $(<$($trait_argument),+>)?>() {
343 let _ = O::$method;
344 }
345
346 verify_method::<$receiver>();
347 };
348}
349
350macro_rules! operation_manifest {
351 (
352 $operation:ty $(as $registry_name:literal)? {
353 method: $trait:ident $(<$($trait_argument:ty),+ $(,)?>)? :: $method:ident;
354 $(policy: $policy:path $(= $owner:ident $access:tt $function:ident($argument:ident))?;)?
355 scope: $scope:ident;
356
357 kernel $first_kernel:tt
358 $(kernel $additional_kernel:tt)*
359 }
360 ) => {
361 $crate::registry::operation_manifest!(
362 @scope $scope,
363 $operation,
364 trait[$trait $(<$($trait_argument),+>)?],
365 $method,
366 name[$($registry_name)? $method],
367 policy[$($policy $(= $owner $access $function($argument))?)?],
368 $first_kernel
369 $($additional_kernel)*
370 );
371 };
372 (@scope element, $($manifest:tt)*) => {
373 $crate::registry::operation_manifest!(
374 @entries operation_element_witness, operation_element_entry, $($manifest)*
375 );
376 };
377 (@scope lane, $($manifest:tt)*) => {
378 $crate::registry::operation_manifest!(
379 @entries operation_lane_witness, operation_lane_entry, $($manifest)*
380 );
381 };
382 (@scope group, $($manifest:tt)*) => {
383 $crate::registry::operation_manifest!(
384 @entries operation_group_witness, operation_group_entry, $($manifest)*
385 );
386 };
387 (
388 @entries $witness:ident, $entry:ident,
389 $operation:ty,
390 trait[$($trait:tt)+],
391 $method:ident,
392 name[$($name:tt)+],
393 policy $policy:tt,
394 $first_kernel:tt
395 $($additional_kernel:tt)*
396 ) => {
397 const _: () = {
398 $crate::registry::$witness!(
399 $operation,
400 $($trait)+,
401 $method,
402 policy $policy,
403 $first_kernel
404 $($additional_kernel)*
405 );
406 };
407
408 pub fn operation_manifest() -> $crate::registry::OperationManifest {
409 $crate::registry::OperationManifest::new(
410 $crate::registry::operation_manifest_name!($($name)+),
411 vec![
412 $crate::registry::$entry!(
413 $operation,
414 $method,
415 policy $policy,
416 $first_kernel,
417 $first_kernel
418 )
419 $(,
420 $crate::registry::$entry!(
421 $operation,
422 $method,
423 policy $policy,
424 $additional_kernel,
425 $additional_kernel
426 )
427 )*
428 ],
429 )
430 }
431 };
432}
433
434pub(crate) use operation_manifest;
435pub(crate) use operation_manifest_name;
436pub(crate) use operation_policy_method;
437
438#[cfg(test)]
439mod test {
440 use super::OperationRegistry;
441 use crate::{
442 AttributeName, Mask, Scalar,
443 cast::{Bool, Int},
444 registry::{
445 ArgumentDescriptor, ArityDescriptor, IndexDescriptor, LaneShapeDescriptor,
446 OperandDescriptor, OrderDescriptor, ValueArgumentDescriptor, ValueDescriptor,
447 },
448 };
449 use graphrecords_core::graphrecord::{GraphRecordValue, NodeIndex};
450
451 fn create_scalar_nodes() -> OperandDescriptor {
452 OperandDescriptor::Lane {
453 shape: LaneShapeDescriptor::Indexed {
454 index: IndexDescriptor::domain::<NodeIndex>(),
455 value: ValueDescriptor::value::<Scalar>(),
456 },
457 arity: ArityDescriptor::Multiple {
458 order: OrderDescriptor::Unordered,
459 },
460 }
461 }
462
463 fn create_mask_nodes() -> OperandDescriptor {
464 OperandDescriptor::Lane {
465 shape: LaneShapeDescriptor::Indexed {
466 index: IndexDescriptor::domain::<NodeIndex>(),
467 value: ValueDescriptor::value::<Mask>(),
468 },
469 arity: ArityDescriptor::Multiple {
470 order: OrderDescriptor::Unordered,
471 },
472 }
473 }
474
475 fn create_attribute_nodes() -> OperandDescriptor {
476 OperandDescriptor::Lane {
477 shape: LaneShapeDescriptor::Indexed {
478 index: IndexDescriptor::domain::<NodeIndex>(),
479 value: ValueDescriptor::value::<AttributeName>(),
480 },
481 arity: ArityDescriptor::Multiple {
482 order: OrderDescriptor::Unordered,
483 },
484 }
485 }
486
487 fn create_mask_values() -> OperandDescriptor {
488 OperandDescriptor::Lane {
489 shape: LaneShapeDescriptor::Bare {
490 value: ValueDescriptor::value::<Mask>(),
491 },
492 arity: ArityDescriptor::Multiple {
493 order: OrderDescriptor::Unordered,
494 },
495 }
496 }
497
498 fn create_scalar_value() -> OperandDescriptor {
499 OperandDescriptor::Lane {
500 shape: LaneShapeDescriptor::Bare {
501 value: ValueDescriptor::value::<Scalar>(),
502 },
503 arity: ArityDescriptor::Single,
504 }
505 }
506
507 fn create_grouped_scalar_nodes() -> OperandDescriptor {
508 OperandDescriptor::Group {
509 member: IndexDescriptor::domain::<NodeIndex>(),
510 key: IndexDescriptor::domain::<GraphRecordValue>(),
511 payload: Box::new(create_scalar_nodes()),
512 }
513 }
514
515 fn create_grouped_mask_nodes() -> OperandDescriptor {
516 OperandDescriptor::Group {
517 member: IndexDescriptor::domain::<NodeIndex>(),
518 key: IndexDescriptor::domain::<GraphRecordValue>(),
519 payload: Box::new(create_mask_nodes()),
520 }
521 }
522
523 fn create_grouped_scalar_value() -> OperandDescriptor {
524 OperandDescriptor::Group {
525 member: IndexDescriptor::domain::<NodeIndex>(),
526 key: IndexDescriptor::domain::<GraphRecordValue>(),
527 payload: Box::new(create_scalar_value()),
528 }
529 }
530
531 #[test]
532 fn test_resolve() {
533 let registry = OperationRegistry::builtins();
534
535 assert_eq!(
536 Some(create_scalar_value()),
537 registry.resolve("sum", &create_scalar_nodes())
538 );
539 assert_eq!(
540 Some(create_scalar_value()),
541 registry.resolve("max", &create_scalar_nodes())
542 );
543 assert_eq!(
544 Some(create_mask_nodes()),
545 registry.resolve("is_duplicated", &create_scalar_nodes())
546 );
547 assert_eq!(
548 Some(create_mask_values()),
549 registry.resolve("is_duplicated", &create_mask_values())
550 );
551 }
552
553 #[test]
554 fn test_invalid_resolve() {
555 let registry = OperationRegistry::builtins();
556
557 assert_eq!(None, registry.resolve("lorem", &create_scalar_nodes()));
559
560 assert_eq!(None, registry.resolve("sum", &create_mask_values()));
562
563 assert_eq!(None, registry.resolve("first", &create_scalar_nodes()));
565
566 assert_eq!(
568 None,
569 registry.resolve("discard_index", &create_mask_values())
570 );
571 }
572
573 #[test]
574 fn test_resolve_with_arguments() {
575 let registry = OperationRegistry::builtins();
576
577 assert_eq!(
578 Some(create_attribute_nodes()),
579 registry.resolve_with_arguments(
580 "cast",
581 &create_attribute_nodes(),
582 &[ArgumentDescriptor::selector::<Int>()],
583 )
584 );
585 assert_eq!(
586 Some(create_grouped_scalar_nodes()),
587 registry.resolve_with_arguments(
588 "group_by",
589 &create_scalar_nodes(),
590 &[ArgumentDescriptor::Value(ValueArgumentDescriptor::literal(
591 ValueDescriptor::value::<Scalar>()
592 ))],
593 )
594 );
595 assert_eq!(
596 Some(create_mask_nodes()),
597 registry.resolve_with_arguments(
598 "is_in",
599 &create_scalar_nodes(),
600 &[ArgumentDescriptor::Value(ValueArgumentDescriptor::literal(
601 ValueDescriptor::value::<Scalar>()
602 ))],
603 )
604 );
605 }
606
607 #[test]
608 fn test_invalid_resolve_with_arguments() {
609 let registry = OperationRegistry::builtins();
610
611 assert_eq!(None, registry.resolve("cast", &create_attribute_nodes()));
613
614 assert_eq!(
616 None,
617 registry.resolve_with_arguments(
618 "cast",
619 &create_attribute_nodes(),
620 &[ArgumentDescriptor::selector::<Bool>()],
621 )
622 );
623
624 assert_eq!(None, registry.resolve("group_by", &create_scalar_nodes()));
626
627 assert_eq!(None, registry.resolve("is_in", &create_scalar_nodes()));
629 }
630
631 #[test]
632 fn test_resolve_group() {
633 let registry = OperationRegistry::builtins();
634
635 assert_eq!(
636 Some(create_grouped_scalar_value()),
637 registry.resolve("sum", &create_grouped_scalar_nodes())
638 );
639 assert_eq!(
640 Some(create_grouped_mask_nodes()),
641 registry.resolve("is_duplicated", &create_grouped_scalar_nodes())
642 );
643 }
644
645 #[test]
646 fn test_invalid_resolve_group() {
647 let registry = OperationRegistry::builtins();
648
649 assert_eq!(None, registry.resolve("sum", &create_grouped_mask_nodes()));
651
652 assert_eq!(
654 None,
655 registry.resolve("lorem", &create_grouped_scalar_nodes())
656 );
657 }
658
659 #[test]
660 fn test_method_names() {
661 let registry = OperationRegistry::builtins();
662
663 let method_names: Vec<_> = registry.method_names().collect();
664
665 assert_eq!(127, method_names.len());
666 assert!(method_names.contains(&"sum"));
667 assert!(method_names.contains(&"add"));
668 assert!(method_names.contains(&"equal_to"));
669 assert!(method_names.contains(&"cast"));
670 assert!(method_names.contains(&"on_error_raise"));
671 assert!(method_names.contains(&"group_by"));
672 assert!(method_names.contains(&"index"));
673 assert!(method_names.contains(&"is_null"));
674 assert!(method_names.contains(&"and"));
675 assert!(method_names.contains(&"is_in"));
676 assert!(method_names.contains(&"abs"));
677 assert!(method_names.contains(&"sort"));
678 assert!(method_names.contains(&"uppercase"));
679 assert!(method_names.contains(&"attribute"));
680 assert!(method_names.contains(&"neighbors"));
681 assert!(method_names.contains(&"unique"));
682 }
683}