drasi_core/path_solver/
solution.rs

1// Copyright 2024 The Drasi Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use hashers::jenkins::spooky_hash::SpookyHasher;
16
17use crate::evaluation::context::QueryVariables;
18
19use std::hash::{Hash, Hasher};
20
21use std::collections::VecDeque;
22
23use crate::models::Element;
24
25use std::sync::Arc;
26
27use std::collections::BTreeMap;
28
29use super::match_path::MatchPath;
30
31pub(crate) type SolutionSignature = u64;
32
33#[derive(Clone, Debug)]
34pub struct MatchPathSolution {
35    pub(crate) solved_slots: BTreeMap<usize, Arc<Element>>,
36    pub(crate) total_slots: usize,
37    pub(crate) queued_slots: Vec<bool>,
38    pub(crate) slot_cursors: VecDeque<(usize, Arc<Element>)>,
39    pub(crate) solution_signature: Option<SolutionSignature>,
40}
41
42impl MatchPathSolution {
43    pub fn new(total_slots: usize) -> Self {
44        let mut queued_slots = Vec::new();
45        queued_slots.resize(total_slots, false);
46
47        MatchPathSolution {
48            solved_slots: BTreeMap::new(),
49            total_slots,
50            queued_slots,
51            slot_cursors: VecDeque::new(),
52            solution_signature: None,
53        }
54    }
55
56    pub fn mark_slot_solved(&mut self, slot_num: usize, value: Arc<Element>) {
57        self.solved_slots.insert(slot_num, value);
58        if self.solved_slots.len() == self.total_slots {
59            let mut hasher = SpookyHasher::default();
60            for (slot_num, value) in &self.solved_slots {
61                slot_num.hash(&mut hasher);
62                let elem_ref = value.get_reference();
63                elem_ref.source_id.hash(&mut hasher);
64                elem_ref.element_id.hash(&mut hasher);
65            }
66            self.solution_signature = Some(hasher.finish());
67        }
68    }
69
70    pub fn enqueue_slot(&mut self, slot_num: usize, value: Arc<Element>) {
71        if !self.queued_slots[slot_num] {
72            self.slot_cursors.push_back((slot_num, value));
73            self.queued_slots[slot_num] = true;
74        }
75    }
76
77    pub fn is_slot_solved(&self, slot_num: usize) -> bool {
78        self.solved_slots.contains_key(&slot_num)
79    }
80
81    pub fn get_solution_signature(&self) -> Option<SolutionSignature> {
82        self.solution_signature
83    }
84
85    #[allow(clippy::explicit_counter_loop)]
86    pub fn into_query_variables(
87        &self,
88        match_path: &MatchPath,
89        base_variables: &QueryVariables,
90    ) -> QueryVariables {
91        let mut result = base_variables.clone();
92        let mut slot_num = 0;
93        for slot in &match_path.slots {
94            match self.solved_slots.get(&slot_num) {
95                Some(element) => {
96                    if let Some(annotation) = &slot.spec.annotation {
97                        result.insert(
98                            annotation.to_string().into_boxed_str(),
99                            element.to_expression_variable(),
100                        );
101                    }
102                }
103                None => {
104                    //log warning
105                }
106            }
107            slot_num += 1;
108        }
109        result
110    }
111}