Skip to main content

graphrecords_query/registry/manifest/
mod.rs

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        // Resolving a method that is not registered should fail
558        assert_eq!(None, registry.resolve("lorem", &create_scalar_nodes()));
559
560        // Summing masks should fail
561        assert_eq!(None, registry.resolve("sum", &create_mask_values()));
562
563        // Taking the first element of an unordered lane should fail
564        assert_eq!(None, registry.resolve("first", &create_scalar_nodes()));
565
566        // Discarding the index of a bare lane should fail
567        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        // Casting without a cast target should fail
612        assert_eq!(None, registry.resolve("cast", &create_attribute_nodes()));
613
614        // Casting attribute names to booleans should fail
615        assert_eq!(
616            None,
617            registry.resolve_with_arguments(
618                "cast",
619                &create_attribute_nodes(),
620                &[ArgumentDescriptor::selector::<Bool>()],
621            )
622        );
623
624        // Grouping without a key should fail
625        assert_eq!(None, registry.resolve("group_by", &create_scalar_nodes()));
626
627        // Testing membership without a set should fail
628        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        // Summing a group of masks should fail
650        assert_eq!(None, registry.resolve("sum", &create_grouped_mask_nodes()));
651
652        // Resolving a method that is not registered should fail on a group too
653        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}