1use anyhow::Result;
7use std::collections::{HashMap, HashSet};
8
9use crate::changes_simplifier::simplify_changes;
10use crate::error::LinkError;
11use crate::link::Link;
12use crate::link_reference_validator::LinkReferenceValidator;
13use crate::lino_link::LinoLink;
14use crate::named_type_links::NamedTypeLinks;
15use crate::parser::Parser;
16use crate::query_types::{Pattern, ResolvedLink};
17
18pub struct QueryProcessor {
21 trace: bool,
22 auto_create_missing_references: bool,
23}
24
25impl QueryProcessor {
26 pub fn new(trace: bool) -> Self {
28 Self {
29 trace,
30 auto_create_missing_references: false,
31 }
32 }
33
34 pub fn with_auto_create_missing_references(
35 mut self,
36 auto_create_missing_references: bool,
37 ) -> Self {
38 self.auto_create_missing_references = auto_create_missing_references;
39 self
40 }
41
42 pub fn process_query(
44 &self,
45 storage: &mut impl NamedTypeLinks,
46 query: &str,
47 ) -> Result<Vec<(Option<Link>, Option<Link>)>> {
48 self.trace_msg(&format!("[ProcessQuery] Query: \"{}\"", query));
49
50 let query = query.trim();
51 if query.is_empty() {
52 self.trace_msg("[ProcessQuery] Query is empty, returning.");
53 return Ok(vec![]);
54 }
55
56 let parser = Parser::new();
57 let parsed_links = parser.parse(query)?;
58
59 self.trace_msg(&format!(
60 "[ProcessQuery] Parser returned {} top-level link(s).",
61 parsed_links.len()
62 ));
63
64 if parsed_links.is_empty() {
65 self.trace_msg("[ProcessQuery] No top-level parsed links found, returning.");
66 return Ok(vec![]);
67 }
68
69 let (restriction_link, substitution_link) = match &parsed_links[0].values {
72 Some(values) if values.len() >= 2 => (&values[0], &values[1]),
73 _ if parsed_links.len() >= 2 => (&parsed_links[0], &parsed_links[1]),
74 _ => {
75 self.trace_msg("[ProcessQuery] Query has fewer than 2 links, returning.");
76 return Ok(vec![]);
77 }
78 };
79
80 self.trace_msg(&format!(
81 "[ProcessQuery] Restriction link => Id={:?} Values.Count={}",
82 restriction_link.id,
83 restriction_link.values_count()
84 ));
85 self.trace_msg(&format!(
86 "[ProcessQuery] Substitution link => Id={:?} Values.Count={}",
87 substitution_link.id,
88 substitution_link.values_count()
89 ));
90
91 let mut changes_list = Vec::new();
92
93 if restriction_link.is_empty() && substitution_link.is_empty() {
95 self.trace_msg(
96 "[ProcessQuery] Restriction & substitution both empty => no operation, returning.",
97 );
98 return Ok(vec![]);
99 }
100
101 if restriction_link.is_empty() && !substitution_link.is_empty() {
103 self.trace_msg(
104 "[ProcessQuery] No restriction, but substitution is non-empty => creation scenario.",
105 );
106 if let Some(values) = &substitution_link.values {
107 changes_list.extend(
108 self.validate_links_exist_or_will_be_created(storage, &[], values)?
109 .into_iter()
110 .map(|link| (None, Some(link))),
111 );
112
113 for link_to_create in values {
114 let created_id = self.ensure_link_created(storage, link_to_create)?;
115 self.trace_msg(&format!(
116 "[ProcessQuery] Created link ID #{} from substitution pattern.",
117 created_id
118 ));
119 if let Some(link) = storage.get_link(created_id) {
120 changes_list.push((None, Some(link)));
121 }
122 }
123 }
124 storage.save()?;
125 return Ok(changes_list);
126 }
127
128 if !restriction_link.is_empty() && substitution_link.is_empty() {
130 self.trace_msg(
131 "[ProcessQuery] Restriction non-empty, substitution empty => deletion scenario.",
132 );
133 let restriction_values = restriction_link.values.as_deref().unwrap_or(&[]);
134 changes_list.extend(
135 self.validate_links_exist_or_will_be_created(storage, restriction_values, &[])?
136 .into_iter()
137 .map(|link| (None, Some(link))),
138 );
139
140 let restriction_patterns = self.patterns_from_lino(restriction_link);
141 let mut links_to_delete = Vec::new();
142 for pattern in &restriction_patterns {
143 links_to_delete.extend(self.matched_links(storage, pattern, &HashMap::new())?);
144 }
145 links_to_delete.sort_by_key(|link| link.index);
146 links_to_delete.dedup_by_key(|link| link.index);
147
148 for link in links_to_delete {
149 if storage.exists(link.index) {
150 let before = storage.delete(link.index)?;
151 changes_list.push((Some(before), None));
152 self.trace_msg(&format!("[ProcessQuery] Deleted link ID #{}.", link.index));
153 }
154 }
155 storage.save()?;
156 return Ok(changes_list);
157 }
158
159 self.trace_msg(
161 "[ProcessQuery] Both restriction and substitution non-empty => update/mixed scenario.",
162 );
163
164 let restriction_patterns = self.patterns_from_lino(restriction_link);
165 let substitution_patterns = self.patterns_from_lino(substitution_link);
166 let restriction_values = restriction_link.values.as_deref().unwrap_or(&[]);
167 let substitution_values = substitution_link.values.as_deref().unwrap_or(&[]);
168 changes_list.extend(
169 self.validate_links_exist_or_will_be_created(
170 storage,
171 restriction_values,
172 substitution_values,
173 )?
174 .into_iter()
175 .map(|link| (None, Some(link))),
176 );
177 let solutions = self.find_all_solutions(storage, &restriction_patterns)?;
178
179 if solutions.is_empty() {
180 self.trace_msg("[ProcessQuery] No solutions found => returning.");
181 if !changes_list.is_empty() {
182 storage.save()?;
183 }
184 return Ok(changes_list);
185 }
186
187 let mut all_solutions_no_operation = true;
188 for solution in &solutions {
189 if !self.solution_is_no_operation(
190 storage,
191 solution,
192 &restriction_patterns,
193 &substitution_patterns,
194 )? {
195 all_solutions_no_operation = false;
196 break;
197 }
198 }
199
200 if all_solutions_no_operation {
201 for solution in &solutions {
202 for pattern in &restriction_patterns {
203 for link in self.matched_links(storage, pattern, solution)? {
204 if !changes_list.contains(&(Some(link), Some(link))) {
205 changes_list.push((Some(link), Some(link)));
206 }
207 }
208 }
209 }
210 return Ok(changes_list);
211 }
212
213 for solution in &solutions {
214 let restriction_links =
215 self.resolve_patterns(storage, &restriction_patterns, solution, false)?;
216 let substitution_links =
217 self.resolve_patterns(storage, &substitution_patterns, solution, true)?;
218 let operations = self.determine_operations(&restriction_links, &substitution_links);
219 for (before, after) in operations {
220 self.apply_operation(storage, before, after, &mut changes_list)?;
221 }
222 }
223
224 storage.save()?;
225
226 let simplified = self.simplify_changes_list(&changes_list);
228
229 Ok(simplified)
230 }
231
232 fn validate_links_exist_or_will_be_created(
233 &self,
234 storage: &mut impl NamedTypeLinks,
235 restriction_patterns: &[LinoLink],
236 substitution_patterns: &[LinoLink],
237 ) -> Result<Vec<Link>> {
238 LinkReferenceValidator::new(self.trace, self.auto_create_missing_references)
239 .validate_links_exist_or_will_be_created(
240 storage,
241 restriction_patterns,
242 substitution_patterns,
243 )
244 }
245
246 fn patterns_from_lino(&self, lino_link: &LinoLink) -> Vec<Pattern> {
247 let mut patterns = lino_link
248 .values
249 .as_ref()
250 .map(|values| {
251 values
252 .iter()
253 .map(Self::create_pattern_from_lino)
254 .collect::<Vec<_>>()
255 })
256 .unwrap_or_default();
257
258 if lino_link.id.is_some() {
259 patterns.insert(0, Self::create_pattern_from_lino(lino_link));
260 }
261
262 patterns
263 }
264
265 fn create_pattern_from_lino(lino_link: &LinoLink) -> Pattern {
266 let index = lino_link.id.clone().unwrap_or_default();
267 match &lino_link.values {
268 Some(values) if values.len() == 2 => Pattern::new(
269 index,
270 Some(Self::create_pattern_from_lino(&values[0])),
271 Some(Self::create_pattern_from_lino(&values[1])),
272 ),
273 _ => Pattern::new(index, None, None),
274 }
275 }
276
277 fn find_all_solutions(
278 &self,
279 storage: &mut impl NamedTypeLinks,
280 patterns: &[Pattern],
281 ) -> Result<Vec<HashMap<String, u32>>> {
282 let mut partial_solutions = vec![HashMap::new()];
283
284 for pattern in patterns {
285 let mut new_solutions = Vec::new();
286 for solution in &partial_solutions {
287 for match_solution in self.match_pattern(storage, pattern, solution)? {
288 if Self::solutions_are_compatible(solution, &match_solution) {
289 let mut combined = solution.clone();
290 combined.extend(match_solution);
291 new_solutions.push(combined);
292 }
293 }
294 }
295 partial_solutions = new_solutions;
296 if partial_solutions.is_empty() {
297 break;
298 }
299 }
300
301 Ok(partial_solutions)
302 }
303
304 fn solutions_are_compatible(
305 existing: &HashMap<String, u32>,
306 new_assignments: &HashMap<String, u32>,
307 ) -> bool {
308 new_assignments
309 .iter()
310 .all(|(key, value)| existing.get(key).is_none_or(|existing| existing == value))
311 }
312
313 fn match_pattern(
314 &self,
315 storage: &mut impl NamedTypeLinks,
316 pattern: &Pattern,
317 current_solution: &HashMap<String, u32>,
318 ) -> Result<Vec<HashMap<String, u32>>> {
319 if pattern.is_leaf() {
320 let resolved_index =
321 self.resolve_match_id(storage, &pattern.index, current_solution)?;
322 return Ok(storage
323 .all_links()
324 .into_iter()
325 .filter(|link| Self::is_any(resolved_index) || link.index == resolved_index)
326 .map(|link| {
327 let mut assignments = HashMap::new();
328 Self::assign_variable(&pattern.index, link.index, &mut assignments);
329 assignments
330 })
331 .collect());
332 }
333
334 let resolved_index = self.resolve_match_id(storage, &pattern.index, current_solution)?;
335
336 if !Self::is_variable(&pattern.index)
337 && !Self::is_any(resolved_index)
338 && resolved_index != 0
339 && storage.exists(resolved_index)
340 {
341 let link = storage.get_link(resolved_index).unwrap();
342 return self.match_link_against_pattern(storage, pattern, link, current_solution);
343 }
344
345 let mut results = Vec::new();
346 for link in storage.all_links() {
347 results.extend(self.match_link_against_pattern(
348 storage,
349 pattern,
350 link,
351 current_solution,
352 )?);
353 }
354 Ok(results)
355 }
356
357 fn match_link_against_pattern(
358 &self,
359 storage: &mut impl NamedTypeLinks,
360 pattern: &Pattern,
361 link: Link,
362 current_solution: &HashMap<String, u32>,
363 ) -> Result<Vec<HashMap<String, u32>>> {
364 if !self.check_id_match(storage, &pattern.index, link.index, current_solution)? {
365 return Ok(Vec::new());
366 }
367
368 let mut results = Vec::new();
369 let source_matches = self.recursive_match_subpattern(
370 storage,
371 pattern.source.as_deref(),
372 link.source,
373 current_solution,
374 )?;
375
376 for source_solution in source_matches {
377 let target_matches = self.recursive_match_subpattern(
378 storage,
379 pattern.target.as_deref(),
380 link.target,
381 &source_solution,
382 )?;
383 for mut target_solution in target_matches {
384 Self::assign_variable(&pattern.index, link.index, &mut target_solution);
385 results.push(target_solution);
386 }
387 }
388
389 Ok(results)
390 }
391
392 fn recursive_match_subpattern(
393 &self,
394 storage: &mut impl NamedTypeLinks,
395 pattern: Option<&Pattern>,
396 link_id: u32,
397 current_solution: &HashMap<String, u32>,
398 ) -> Result<Vec<HashMap<String, u32>>> {
399 let Some(pattern) = pattern else {
400 return Ok(vec![current_solution.clone()]);
401 };
402
403 if pattern.is_leaf() {
404 if self.check_id_match(storage, &pattern.index, link_id, current_solution)? {
405 let mut solution = current_solution.clone();
406 Self::assign_variable(&pattern.index, link_id, &mut solution);
407 return Ok(vec![solution]);
408 }
409 return Ok(Vec::new());
410 }
411
412 let Some(link) = storage.get_link(link_id) else {
413 return Ok(Vec::new());
414 };
415
416 self.match_link_against_pattern(storage, pattern, link, current_solution)
417 }
418
419 fn check_id_match(
420 &self,
421 storage: &mut impl NamedTypeLinks,
422 pattern_id: &str,
423 candidate_id: u32,
424 current_solution: &HashMap<String, u32>,
425 ) -> Result<bool> {
426 if pattern_id.is_empty() || pattern_id == "*" {
427 return Ok(true);
428 }
429
430 if Self::is_variable(pattern_id) {
431 return Ok(current_solution
432 .get(pattern_id)
433 .is_none_or(|existing| *existing == candidate_id));
434 }
435
436 if let Ok(parsed) = pattern_id.parse::<u32>() {
437 return Ok(parsed == candidate_id);
438 }
439
440 Ok(storage
441 .get_by_name(pattern_id)?
442 .is_some_and(|named_id| named_id == candidate_id))
443 }
444
445 fn resolve_match_id(
446 &self,
447 storage: &mut impl NamedTypeLinks,
448 identifier: &str,
449 current_solution: &HashMap<String, u32>,
450 ) -> Result<u32> {
451 if identifier.is_empty() || identifier == "*" {
452 return Ok(u32::MAX);
453 }
454 if let Some(value) = current_solution.get(identifier) {
455 return Ok(*value);
456 }
457 if Self::is_variable(identifier) {
458 return Ok(u32::MAX);
459 }
460 if let Ok(parsed) = identifier.parse::<u32>() {
461 return Ok(parsed);
462 }
463 Ok(storage.get_by_name(identifier)?.unwrap_or(0))
464 }
465
466 fn matched_links(
467 &self,
468 storage: &mut impl NamedTypeLinks,
469 pattern: &Pattern,
470 solution: &HashMap<String, u32>,
471 ) -> Result<Vec<Link>> {
472 if pattern.is_leaf() {
473 let resolved_index = self.resolve_match_id(storage, &pattern.index, solution)?;
474 return Ok(storage
475 .all_links()
476 .into_iter()
477 .filter(|link| Self::is_any(resolved_index) || link.index == resolved_index)
478 .collect());
479 }
480
481 let mut links = Vec::new();
482 for matched_solution in self.match_pattern(storage, pattern, solution)? {
483 if let Some(definition) =
484 self.resolve_pattern_readonly(storage, pattern, &matched_solution, false)?
485 {
486 links.extend(self.links_matching_definition(storage, &definition)?);
487 }
488 }
489 Ok(links)
490 }
491
492 fn solution_is_no_operation(
493 &self,
494 storage: &mut impl NamedTypeLinks,
495 solution: &HashMap<String, u32>,
496 restrictions: &[Pattern],
497 substitutions: &[Pattern],
498 ) -> Result<bool> {
499 let mut restriction_links = self
500 .resolve_patterns_readonly(storage, restrictions, solution, false)?
501 .into_iter()
502 .map(|definition| definition.to_link())
503 .collect::<Vec<_>>();
504 let mut substitution_links = self
505 .resolve_patterns_readonly(storage, substitutions, solution, true)?
506 .into_iter()
507 .map(|definition| definition.to_link())
508 .collect::<Vec<_>>();
509
510 restriction_links.sort_by_key(|link| link.index);
511 substitution_links.sort_by_key(|link| link.index);
512
513 Ok(restriction_links == substitution_links)
514 }
515
516 fn resolve_patterns_readonly(
517 &self,
518 storage: &mut impl NamedTypeLinks,
519 patterns: &[Pattern],
520 solution: &HashMap<String, u32>,
521 is_substitution: bool,
522 ) -> Result<Vec<ResolvedLink>> {
523 let mut resolved = Vec::new();
524 for pattern in patterns {
525 if let Some(link) =
526 self.resolve_pattern_readonly(storage, pattern, solution, is_substitution)?
527 {
528 resolved.push(link);
529 }
530 }
531 Ok(resolved)
532 }
533
534 fn resolve_pattern_readonly(
535 &self,
536 storage: &mut impl NamedTypeLinks,
537 pattern: &Pattern,
538 solution: &HashMap<String, u32>,
539 is_substitution: bool,
540 ) -> Result<Option<ResolvedLink>> {
541 if pattern.is_leaf() {
542 let index = self.resolve_identifier_readonly(
543 storage,
544 &pattern.index,
545 solution,
546 if is_substitution { 0 } else { u32::MAX },
547 )?;
548 return Ok(Some(ResolvedLink::new(index, u32::MAX, u32::MAX, None)));
549 }
550
551 let source_pattern = pattern
552 .source
553 .as_deref()
554 .ok_or_else(|| LinkError::InvalidFormat("Invalid source pattern".to_string()))?;
555 let target_pattern = pattern
556 .target
557 .as_deref()
558 .ok_or_else(|| LinkError::InvalidFormat("Invalid target pattern".to_string()))?;
559
560 let source = self
561 .resolve_pattern_readonly(storage, source_pattern, solution, is_substitution)?
562 .ok_or_else(|| LinkError::InvalidFormat("Invalid source pattern".to_string()))?
563 .index;
564 let target = self
565 .resolve_pattern_readonly(storage, target_pattern, solution, is_substitution)?
566 .ok_or_else(|| LinkError::InvalidFormat("Invalid target pattern".to_string()))?
567 .index;
568 let default_index = if is_substitution { 0 } else { u32::MAX };
569 let index =
570 self.resolve_identifier_readonly(storage, &pattern.index, solution, default_index)?;
571
572 Ok(Some(ResolvedLink::new(index, source, target, None)))
573 }
574
575 fn resolve_identifier_readonly(
576 &self,
577 storage: &mut impl NamedTypeLinks,
578 identifier: &str,
579 solution: &HashMap<String, u32>,
580 default_value: u32,
581 ) -> Result<u32> {
582 if identifier.is_empty() {
583 return Ok(default_value);
584 }
585 if identifier == "*" {
586 return Ok(u32::MAX);
587 }
588 if let Some(value) = solution.get(identifier) {
589 return Ok(*value);
590 }
591 if Self::is_variable(identifier) {
592 return Ok(default_value);
593 }
594 if let Ok(parsed) = identifier.parse::<u32>() {
595 return Ok(parsed);
596 }
597 Ok(storage.get_by_name(identifier)?.unwrap_or(default_value))
598 }
599
600 fn resolve_patterns(
601 &self,
602 storage: &mut impl NamedTypeLinks,
603 patterns: &[Pattern],
604 solution: &HashMap<String, u32>,
605 is_substitution: bool,
606 ) -> Result<Vec<ResolvedLink>> {
607 let mut working_solution = solution.clone();
608 let mut visited_indexes = HashSet::new();
609 let mut resolved = Vec::new();
610 for pattern in patterns {
611 resolved.push(self.resolve_pattern(
612 storage,
613 pattern,
614 &mut working_solution,
615 is_substitution,
616 &mut visited_indexes,
617 )?);
618 }
619 Ok(resolved)
620 }
621
622 fn resolve_pattern(
623 &self,
624 storage: &mut impl NamedTypeLinks,
625 pattern: &Pattern,
626 solution: &mut HashMap<String, u32>,
627 is_substitution: bool,
628 visited_indexes: &mut HashSet<u32>,
629 ) -> Result<ResolvedLink> {
630 if pattern.is_leaf() {
631 let index = self.resolve_identifier(
632 storage,
633 &pattern.index,
634 solution,
635 if is_substitution { 0 } else { u32::MAX },
636 is_substitution,
637 )?;
638 return Ok(ResolvedLink::new(index, u32::MAX, u32::MAX, None));
639 }
640
641 let mut source = self
642 .resolve_pattern(
643 storage,
644 pattern.source.as_deref().unwrap(),
645 solution,
646 is_substitution,
647 visited_indexes,
648 )?
649 .index;
650 let mut target = self
651 .resolve_pattern(
652 storage,
653 pattern.target.as_deref().unwrap(),
654 solution,
655 is_substitution,
656 visited_indexes,
657 )?
658 .index;
659 let default_index = if is_substitution { 0 } else { u32::MAX };
660 let mut index =
661 self.resolve_identifier(storage, &pattern.index, solution, default_index, false)?;
662 let mut name = None;
663
664 if is_substitution
665 && !pattern.index.is_empty()
666 && !Self::is_numeric_or_wildcard(&pattern.index)
667 && !Self::is_variable(&pattern.index)
668 {
669 name = Some(pattern.index.clone());
670 if index == 0 {
671 if let Some(existing_id) = storage.search(source, target) {
672 index = existing_id;
673 }
674 }
675 }
676
677 if is_substitution {
678 Self::preserve_existing_substitution_parts(
679 storage,
680 pattern,
681 solution,
682 index,
683 &mut source,
684 &mut target,
685 visited_indexes,
686 )?;
687 }
688
689 Ok(ResolvedLink::new(index, source, target, name))
690 }
691
692 fn resolve_identifier(
693 &self,
694 storage: &mut impl NamedTypeLinks,
695 identifier: &str,
696 solution: &HashMap<String, u32>,
697 default_value: u32,
698 create_named_leaf: bool,
699 ) -> Result<u32> {
700 if identifier.is_empty() {
701 return Ok(default_value);
702 }
703 if identifier == "*" {
704 return Ok(u32::MAX);
705 }
706 if let Some(value) = solution.get(identifier) {
707 return Ok(*value);
708 }
709 if Self::is_variable(identifier) {
710 return Ok(default_value);
711 }
712 if let Ok(parsed) = identifier.parse::<u32>() {
713 return Ok(parsed);
714 }
715 if let Some(named_id) = storage.get_by_name(identifier)? {
716 return Ok(named_id);
717 }
718 if create_named_leaf {
719 return storage.get_or_create_named(identifier);
720 }
721 Ok(default_value)
722 }
723
724 fn determine_operations(
725 &self,
726 restrictions: &[ResolvedLink],
727 substitutions: &[ResolvedLink],
728 ) -> Vec<(Option<ResolvedLink>, Option<ResolvedLink>)> {
729 let mut operations = Vec::new();
730 let mut restriction_by_index = HashMap::new();
731 let mut substitution_by_index = HashMap::new();
732 let mut wildcard_restrictions = Vec::new();
733 let mut wildcard_substitutions = Vec::new();
734
735 for restriction in restrictions {
736 if Self::is_normal_index(restriction.index) {
737 restriction_by_index.insert(restriction.index, restriction.clone());
738 } else {
739 wildcard_restrictions.push(restriction.clone());
740 }
741 }
742
743 for substitution in substitutions {
744 if Self::is_normal_index(substitution.index) {
745 substitution_by_index.insert(substitution.index, substitution.clone());
746 } else {
747 wildcard_substitutions.push(substitution.clone());
748 }
749 }
750
751 let mut all_indices = restriction_by_index
752 .keys()
753 .chain(substitution_by_index.keys())
754 .copied()
755 .collect::<Vec<_>>();
756 all_indices.sort_unstable();
757 all_indices.dedup();
758
759 for index in all_indices {
760 match (
761 restriction_by_index.get(&index),
762 substitution_by_index.get(&index),
763 ) {
764 (Some(before), Some(after)) => {
765 operations.push((Some(before.clone()), Some(after.clone())));
766 }
767 (Some(before), None) => operations.push((Some(before.clone()), None)),
768 (None, Some(after)) => operations.push((None, Some(after.clone()))),
769 (None, None) => {}
770 }
771 }
772
773 operations.extend(
774 wildcard_restrictions
775 .into_iter()
776 .map(|restriction| (Some(restriction), None)),
777 );
778 operations.extend(
779 wildcard_substitutions
780 .into_iter()
781 .map(|substitution| (None, Some(substitution))),
782 );
783
784 operations
785 }
786
787 fn apply_operation(
788 &self,
789 storage: &mut impl NamedTypeLinks,
790 before: Option<ResolvedLink>,
791 after: Option<ResolvedLink>,
792 changes: &mut Vec<(Option<Link>, Option<Link>)>,
793 ) -> Result<()> {
794 match (before, after) {
795 (Some(before), None) => {
796 let mut links = self.links_matching_definition(storage, &before)?;
797 links.sort_by_key(|link| link.index);
798 links.dedup_by_key(|link| link.index);
799 for link in links {
800 if storage.exists(link.index) {
801 let deleted = storage.delete(link.index)?;
802 changes.push((Some(deleted), None));
803 }
804 }
805 }
806 (None, Some(after)) => {
807 let created = self.create_or_update_resolved_link(storage, &after)?;
808 changes.push((None, Some(created)));
809 }
810 (Some(before), Some(after)) => {
811 if before.index == after.index && storage.exists(before.index) {
812 let before_link = storage.get_link(before.index).unwrap();
813 if before_link.source != after.source || before_link.target != after.target {
814 storage.update(before.index, after.source, after.target)?;
815 }
816 if let Some(name) = &after.name {
817 storage.set_name(before.index, name)?;
818 }
819 let after_link = storage.get_link(before.index).unwrap();
820 changes.push((Some(before_link), Some(after_link)));
821 } else {
822 self.apply_operation(storage, Some(before), None, changes)?;
823 self.apply_operation(storage, None, Some(after), changes)?;
824 }
825 }
826 (None, None) => {}
827 }
828
829 Ok(())
830 }
831
832 fn create_or_update_resolved_link(
833 &self,
834 storage: &mut impl NamedTypeLinks,
835 definition: &ResolvedLink,
836 ) -> Result<Link> {
837 let id = if Self::is_normal_index(definition.index) {
838 storage.try_ensure_created(definition.index)?;
839 storage.update(definition.index, definition.source, definition.target)?;
840 definition.index
841 } else if let Some(existing_id) = storage.search(definition.source, definition.target) {
842 existing_id
843 } else {
844 storage.create(definition.source, definition.target)
845 };
846
847 if let Some(name) = &definition.name {
848 storage.set_name(id, name)?;
849 }
850
851 Ok(storage.get_link(id).unwrap())
852 }
853
854 fn links_matching_definition(
855 &self,
856 storage: &mut impl NamedTypeLinks,
857 definition: &ResolvedLink,
858 ) -> Result<Vec<Link>> {
859 Ok(storage
860 .all_links()
861 .into_iter()
862 .filter(|link| {
863 (definition.index == 0
864 || Self::is_any(definition.index)
865 || link.index == definition.index)
866 && (Self::is_any(definition.source) || link.source == definition.source)
867 && (Self::is_any(definition.target) || link.target == definition.target)
868 })
869 .collect())
870 }
871
872 fn assign_variable(id: &str, value: u32, assignments: &mut HashMap<String, u32>) {
873 if Self::is_variable(id) && value != 0 {
874 assignments.insert(id.to_string(), value);
875 }
876 }
877
878 fn is_variable(identifier: &str) -> bool {
879 !identifier.is_empty() && identifier.starts_with('$')
880 }
881
882 fn is_any(value: u32) -> bool {
883 value == u32::MAX
884 }
885
886 fn is_normal_index(value: u32) -> bool {
887 value != 0 && !Self::is_any(value)
888 }
889
890 fn is_numeric_or_wildcard(identifier: &str) -> bool {
891 identifier == "*" || identifier.parse::<u32>().is_ok()
892 }
893
894 fn ensure_link_created(
896 &self,
897 storage: &mut impl NamedTypeLinks,
898 lino_link: &LinoLink,
899 ) -> Result<u32> {
900 if !lino_link.has_values() {
902 if let Some(ref id) = lino_link.id {
903 if id == "*" || Self::is_variable(id) {
904 return Ok(u32::MAX);
905 }
906
907 if let Ok(num) = id.parse::<u32>() {
909 return Ok(num);
910 }
911
912 return storage.get_or_create_named(id);
914 }
915 return Ok(0);
916 }
917
918 if lino_link.values_count() == 2 {
920 let values = lino_link.values.as_ref().unwrap();
921
922 let source_id = self.ensure_link_created(storage, &values[0])?;
924 let target_id = self.ensure_link_created(storage, &values[1])?;
925
926 let link_id = if let Some(ref id) = lino_link.id {
928 if let Ok(num) = id.parse::<u32>() {
929 storage.try_ensure_created(num)?;
931 storage.update(num, source_id, target_id)?;
932 num
933 } else if id == "*" || Self::is_variable(id) {
934 storage.get_or_create(source_id, target_id)
935 } else {
936 let existing = storage.get_by_name(id)?;
938 if let Some(id_num) = existing {
939 storage.update(id_num, source_id, target_id)?;
940 id_num
941 } else {
942 let new_id = storage.create(source_id, target_id);
943 storage.set_name(new_id, id)?;
944 new_id
945 }
946 }
947 } else {
948 storage.get_or_create(source_id, target_id)
950 };
951
952 return Ok(link_id);
953 }
954
955 Err(LinkError::InvalidFormat("Invalid link structure".to_string()).into())
956 }
957
958 fn simplify_changes_list(
960 &self,
961 changes: &[(Option<Link>, Option<Link>)],
962 ) -> Vec<(Option<Link>, Option<Link>)> {
963 let mut to_simplify: Vec<(Link, Link)> = Vec::new();
965 let mut non_simplifiable: Vec<(Option<Link>, Option<Link>)> = Vec::new();
966
967 for (before, after) in changes {
968 match (before, after) {
969 (Some(b), Some(a)) => {
970 to_simplify.push((*b, *a));
971 }
972 _ => {
973 non_simplifiable.push((*before, *after));
974 }
975 }
976 }
977
978 let simplified = simplify_changes(to_simplify);
979
980 let mut result: Vec<(Option<Link>, Option<Link>)> = non_simplifiable;
981 for (b, a) in simplified {
982 result.push((Some(b), Some(a)));
983 }
984
985 result
986 }
987
988 fn trace_msg(&self, msg: &str) {
990 if self.trace {
991 eprintln!("{}", msg);
992 }
993 }
994}