1use alloc::{boxed::Box, collections::BTreeMap, string::String, sync::Arc, vec::Vec};
2use core::ops::{Bound, Deref};
3
4use buggy::{bug, Bug, BugExt};
5use vec1::Vec1;
6
7use crate::{
8 Address, Checkpoint, Command, CommandId, Fact, FactIndex, FactPerspective, GraphId, Keys,
9 Location, Perspective, PolicyId, Prior, Priority, Query, QueryMut, Revertable, Segment,
10 Storage, StorageError, StorageProvider,
11};
12
13#[derive(Debug)]
14pub struct MemCommand {
15 priority: Priority,
16 id: CommandId,
17 parent: Prior<Address>,
18 policy: Option<Box<[u8]>>,
19 data: Box<[u8]>,
20 max_cut: usize,
21}
22
23impl MemCommand {
24 fn from_cmd<C: Command>(command: &C, max_cut: usize) -> Self {
25 let policy = command.policy().map(Box::from);
26
27 MemCommand {
28 priority: command.priority(),
29 id: command.id(),
30 parent: command.parent(),
31 policy,
32 data: command.bytes().into(),
33 max_cut,
34 }
35 }
36}
37
38impl Command for MemCommand {
39 fn priority(&self) -> Priority {
40 self.priority.clone()
41 }
42
43 fn id(&self) -> CommandId {
44 self.id
45 }
46
47 fn parent(&self) -> Prior<Address> {
48 self.parent
49 }
50
51 fn policy(&self) -> Option<&[u8]> {
52 self.policy.as_deref()
53 }
54
55 fn bytes(&self) -> &[u8] {
56 &self.data
57 }
58
59 fn max_cut(&self) -> Result<usize, Bug> {
60 Ok(self.max_cut)
61 }
62}
63
64#[derive(Default)]
65pub struct MemStorageProvider {
66 storage: BTreeMap<GraphId, MemStorage>,
67}
68
69impl MemStorageProvider {
70 pub const fn new() -> MemStorageProvider {
71 MemStorageProvider {
72 storage: BTreeMap::new(),
73 }
74 }
75}
76
77impl StorageProvider for MemStorageProvider {
78 type Perspective = MemPerspective;
79 type Storage = MemStorage;
80 type Segment = MemSegment;
81
82 fn new_perspective(&mut self, policy_id: PolicyId) -> Self::Perspective {
83 MemPerspective::new_unrooted(policy_id)
84 }
85
86 fn new_storage(
87 &mut self,
88 update: Self::Perspective,
89 ) -> Result<(GraphId, &mut Self::Storage), StorageError> {
90 use alloc::collections::btree_map::Entry;
91
92 if update.commands.is_empty() {
93 return Err(StorageError::EmptyPerspective);
94 }
95 let graph_id = GraphId::from(update.commands[0].command.id.into_id());
96 let entry = match self.storage.entry(graph_id) {
97 Entry::Vacant(v) => v,
98 Entry::Occupied(_) => return Err(StorageError::StorageExists),
99 };
100
101 let mut storage = MemStorage::new();
102 let segment = storage.write(update)?;
103 storage.commit(segment)?;
104 Ok((graph_id, entry.insert(storage)))
105 }
106
107 fn get_storage(&mut self, graph: GraphId) -> Result<&mut Self::Storage, StorageError> {
108 self.storage
109 .get_mut(&graph)
110 .ok_or(StorageError::NoSuchStorage)
111 }
112
113 fn list_graph_ids(
114 &mut self,
115 ) -> Result<impl Iterator<Item = Result<GraphId, StorageError>>, StorageError> {
116 Ok(self.storage.keys().copied().map(Ok))
117 }
118}
119
120type FactMap = BTreeMap<Keys, Option<Box<[u8]>>>;
121type NamedFactMap = BTreeMap<String, FactMap>;
122
123pub struct MemStorage {
124 segments: Vec<MemSegment>,
125 commands: BTreeMap<CommandId, Location>,
126 head: Option<Location>,
127}
128
129impl MemStorage {
130 fn new() -> Self {
131 Self {
132 segments: Vec::new(),
133 commands: BTreeMap::new(),
134 head: None,
135 }
136 }
137
138 fn new_segment(
139 &mut self,
140 prior: Prior<Location>,
141 policy: PolicyId,
142 mut commands: Vec1<CommandData>,
143 facts: MemFactIndex,
144 max_cut: usize,
145 ) -> Result<MemSegment, StorageError> {
146 let index = self.segments.len();
147
148 for (i, command) in commands.iter_mut().enumerate() {
149 command.command.max_cut = max_cut.checked_add(i).assume("must not overflow")?;
150 }
151
152 let segment = MemSegmentInner {
153 prior,
154 index,
155 policy,
156 commands,
157 facts,
158 };
159
160 let cell = MemSegment::from(segment);
161 self.segments.push(cell.clone());
162
163 Ok(cell)
164 }
165}
166
167impl Drop for MemStorage {
168 fn drop(&mut self) {
171 while self.segments.pop().is_some() {}
172 }
173}
174
175impl Storage for MemStorage {
176 type Perspective = MemPerspective;
177 type Segment = MemSegment;
178 type FactIndex = MemFactIndex;
179 type FactPerspective = MemFactPerspective;
180
181 fn get_command_id(&self, location: Location) -> Result<CommandId, StorageError> {
182 let segment = self.get_segment(location)?;
183 let command = segment
184 .get_command(location)
185 .ok_or(StorageError::CommandOutOfBounds(location))?;
186 Ok(command.id())
187 }
188
189 fn get_linear_perspective(
190 &self,
191 parent: Location,
192 ) -> Result<Option<Self::Perspective>, StorageError> {
193 let segment = self.get_segment(parent)?;
194 let command = segment
195 .get_command(parent)
196 .ok_or(StorageError::CommandOutOfBounds(parent))?;
197 let parent_addr = command.address()?;
198
199 let policy = segment.policy;
200 let prior_facts: FactPerspectivePrior = if parent == segment.head_location() {
201 segment.facts.clone().into()
202 } else {
203 let mut facts = MemFactPerspective::new(segment.facts.prior.clone().into());
204 for data in &segment.commands[..=parent.command] {
205 facts.apply_updates(&data.updates);
206 }
207 if facts.map.is_empty() {
208 facts.prior
209 } else {
210 facts.into()
211 }
212 };
213 let prior = Prior::Single(parent);
214 let parents = Prior::Single(parent_addr);
215
216 let max_cut = self
217 .get_segment(parent)?
218 .get_command(parent)
219 .assume("location must exist")?
220 .max_cut()?
221 .checked_add(1)
222 .assume("must not overflow")?;
223 let perspective = MemPerspective::new(prior, parents, policy, prior_facts, max_cut);
224
225 Ok(Some(perspective))
226 }
227
228 fn get_fact_perspective(
229 &self,
230 location: Location,
231 ) -> Result<Self::FactPerspective, StorageError> {
232 let segment = self.get_segment(location)?;
233
234 if location == segment.head_location()
235 || segment.commands.iter().all(|cmd| cmd.updates.is_empty())
236 {
237 return Ok(MemFactPerspective::new(segment.facts.clone().into()));
238 }
239
240 let mut facts = MemFactPerspective::new(segment.facts.prior.clone().into());
241 for data in &segment.commands[..=location.command] {
242 facts.apply_updates(&data.updates);
243 }
244
245 Ok(facts)
246 }
247
248 fn new_merge_perspective(
249 &self,
250 left: Location,
251 right: Location,
252 _last_common_ancestor: (Location, usize),
253 policy_id: PolicyId,
254 braid: MemFactIndex,
255 ) -> Result<Option<Self::Perspective>, StorageError> {
256 let left_segment = self.get_segment(left)?;
260 let left_policy_id = left_segment.policy;
261 let right_segment = self.get_segment(right)?;
262 let right_policy_id = right_segment.policy;
263
264 if (policy_id != left_policy_id) && (policy_id != right_policy_id) {
265 return Err(StorageError::PolicyMismatch);
266 }
267
268 let prior = Prior::Merge(left, right);
269
270 let left_command = left_segment
271 .get_command(left)
272 .ok_or(StorageError::CommandOutOfBounds(left))?;
273 let right_command = right_segment
274 .get_command(right)
275 .ok_or(StorageError::CommandOutOfBounds(right))?;
276 let parents = Prior::Merge(left_command.address()?, right_command.address()?);
277
278 let left_distance = left_command.max_cut()?;
279 let right_distance = right_command.max_cut()?;
280 let max_cut = left_distance
281 .max(right_distance)
282 .checked_add(1)
283 .assume("must not overflow")?;
284
285 let perspective = MemPerspective::new(prior, parents, policy_id, braid.into(), max_cut);
286
287 Ok(Some(perspective))
288 }
289
290 fn get_segment(&self, location: Location) -> Result<MemSegment, StorageError> {
291 self.segments
292 .get(location.segment)
293 .ok_or(StorageError::SegmentOutOfBounds(location))
294 .cloned()
295 }
296
297 fn get_head(&self) -> Result<Location, StorageError> {
298 Ok(self.head.assume("storage has head after init")?)
299 }
300
301 fn write(&mut self, update: Self::Perspective) -> Result<Self::Segment, StorageError> {
302 let facts = self.write_facts(update.facts)?;
303
304 let commands: Vec1<CommandData> = update
305 .commands
306 .try_into()
307 .map_err(|_| StorageError::EmptyPerspective)?;
308
309 let segment_index = self.segments.len();
310
311 for (command_index, data) in commands.iter().enumerate() {
313 let new_location = Location::new(segment_index, command_index);
314 self.commands.insert(data.command.id(), new_location);
315 }
316
317 let segment =
318 self.new_segment(update.prior, update.policy, commands, facts, update.max_cut)?;
319
320 Ok(segment)
321 }
322
323 fn write_facts(
324 &mut self,
325 facts: Self::FactPerspective,
326 ) -> Result<Self::FactIndex, StorageError> {
327 let prior = match facts.prior {
328 FactPerspectivePrior::None => None,
329 FactPerspectivePrior::FactPerspective(prior) => Some(self.write_facts(*prior)?),
330 FactPerspectivePrior::FactIndex(prior) => Some(prior),
331 };
332 if facts.map.is_empty() {
333 if let Some(prior) = prior {
334 return Ok(prior);
335 }
336 }
337 Ok(MemFactIndex(Arc::new(MemFactsInner {
338 map: facts.map,
339 prior,
340 })))
341 }
342
343 fn commit(&mut self, segment: Self::Segment) -> Result<(), StorageError> {
344 if let Some(head) = self.head {
347 if !self.is_ancestor(head, &segment)? {
348 return Err(StorageError::HeadNotAncestor);
349 }
350 }
351
352 self.head = Some(segment.head_location());
353 Ok(())
354 }
355}
356
357#[derive(Clone, Debug)]
358pub struct MemFactIndex(Arc<MemFactsInner>);
359
360impl Deref for MemFactIndex {
361 type Target = MemFactsInner;
362 fn deref(&self) -> &Self::Target {
363 self.0.deref()
364 }
365}
366
367impl MemFactIndex {
368 #[cfg(all(test, feature = "graphviz"))]
369 fn name(&self) -> String {
370 format!("\"{:p}\"", Arc::as_ptr(&self.0))
371 }
372}
373
374#[derive(Debug)]
375pub struct MemFactsInner {
376 map: NamedFactMap,
377 prior: Option<MemFactIndex>,
378}
379
380pub(crate) fn find_prefixes<'m, 'p: 'm>(
381 map: &'m FactMap,
382 prefix: &'p [Box<[u8]>],
383) -> impl Iterator<Item = (&'m Keys, Option<&'m [u8]>)> + 'm {
384 map.range::<[Box<[u8]>], _>((Bound::Included(prefix), Bound::Unbounded))
385 .take_while(|(k, _)| k.starts_with(prefix))
386 .map(|(k, v)| (k, v.as_deref()))
387}
388
389impl FactIndex for MemFactIndex {}
390impl Query for MemFactIndex {
391 fn query(&self, name: &str, keys: &[Box<[u8]>]) -> Result<Option<Box<[u8]>>, StorageError> {
392 let mut prior = Some(self.deref());
393 while let Some(facts) = prior {
394 if let Some(slot) = facts.map.get(name).and_then(|m| m.get(keys)) {
395 return Ok(slot.as_ref().cloned());
396 }
397 prior = facts.prior.as_deref();
398 }
399 Ok(None)
400 }
401
402 type QueryIterator = Box<dyn Iterator<Item = Result<Fact, StorageError>>>;
403 fn query_prefix(
404 &self,
405 name: &str,
406 prefix: &[Box<[u8]>],
407 ) -> Result<Self::QueryIterator, StorageError> {
408 Ok(Box::from(
409 self.query_prefix_inner(name, prefix)
410 .into_iter()
411 .filter_map(|(key, value)| Some(Ok(Fact { key, value: value? }))),
413 ))
414 }
415}
416
417impl MemFactIndex {
418 fn query_prefix_inner(&self, name: &str, prefix: &[Box<[u8]>]) -> FactMap {
419 let mut matches = BTreeMap::new();
420
421 let mut prior = Some(self.deref());
422 while let Some(facts) = prior {
424 if let Some(map) = facts.map.get(name) {
425 for (k, v) in find_prefixes(map, prefix) {
426 if !matches.contains_key(k) {
428 matches.insert(k.clone(), v.map(Into::into));
429 }
430 }
431 }
432 prior = facts.prior.as_deref();
433 }
434
435 matches
436 }
437}
438
439#[derive(Debug)]
440struct CommandData {
441 command: MemCommand,
442 updates: Vec<Update>,
443}
444
445#[derive(Debug)]
446pub struct MemSegmentInner {
447 index: usize,
448 prior: Prior<Location>,
449 policy: PolicyId,
450 commands: Vec1<CommandData>,
451 facts: MemFactIndex,
452}
453
454#[derive(Clone, Debug)]
455pub struct MemSegment(Arc<MemSegmentInner>);
456
457impl Deref for MemSegment {
458 type Target = MemSegmentInner;
459
460 fn deref(&self) -> &Self::Target {
461 self.0.deref()
462 }
463}
464
465impl From<MemSegmentInner> for MemSegment {
466 fn from(segment: MemSegmentInner) -> Self {
467 MemSegment(Arc::new(segment))
468 }
469}
470
471impl Segment for MemSegment {
472 type FactIndex = MemFactIndex;
473 type Command<'a> = &'a MemCommand;
474
475 fn head(&self) -> Result<&MemCommand, StorageError> {
476 Ok(&self.commands.last().command)
477 }
478
479 fn first(&self) -> &MemCommand {
480 &self.commands.first().command
481 }
482
483 fn head_location(&self) -> Location {
484 Location {
485 segment: self.index,
486 command: self
487 .commands
488 .len()
489 .checked_sub(1)
490 .expect("commands.len() must be > 0"),
491 }
492 }
493
494 fn first_location(&self) -> Location {
495 Location {
496 segment: self.index,
497 command: 0,
498 }
499 }
500
501 fn contains(&self, location: Location) -> bool {
502 location.segment == self.index
503 }
504
505 fn policy(&self) -> PolicyId {
506 self.policy
507 }
508
509 fn prior(&self) -> Prior<Location> {
510 self.prior
511 }
512
513 fn get_command(&self, location: Location) -> Option<&MemCommand> {
514 if location.segment != self.index {
515 return None;
516 }
517
518 self.commands.get(location.command).map(|d| &d.command)
519 }
520
521 fn get_from(&self, location: Location) -> Vec<&MemCommand> {
522 if location.segment != self.index {
523 return Vec::new();
524 }
525
526 self.commands[location.command..]
527 .iter()
528 .map(|d| &d.command)
529 .collect()
530 }
531
532 fn get_from_max_cut(&self, max_cut: usize) -> Result<Option<Location>, StorageError> {
533 for (i, command) in self.commands.iter().enumerate() {
534 if command.command.max_cut == max_cut {
535 return Ok(Some(Location {
536 segment: self.index,
537 command: i,
538 }));
539 }
540 }
541 Ok(None)
542 }
543
544 fn longest_max_cut(&self) -> Result<usize, StorageError> {
545 Ok(self.commands.last().command.max_cut)
546 }
547
548 fn shortest_max_cut(&self) -> usize {
549 self.commands[0].command.max_cut
550 }
551
552 fn skip_list(&self) -> &[(Location, usize)] {
553 &[]
554 }
555
556 fn facts(&self) -> Result<Self::FactIndex, StorageError> {
557 Ok(self.facts.clone())
558 }
559}
560
561type Update = (String, Keys, Option<Box<[u8]>>);
562
563#[derive(Debug)]
564pub struct MemPerspective {
565 prior: Prior<Location>,
566 parents: Prior<Address>,
567 policy: PolicyId,
568 facts: MemFactPerspective,
569 commands: Vec<CommandData>,
570 current_updates: Vec<Update>,
571 max_cut: usize,
572}
573
574#[derive(Debug)]
575enum FactPerspectivePrior {
576 None,
577 FactPerspective(Box<MemFactPerspective>),
578 FactIndex(MemFactIndex),
579}
580
581impl From<MemFactIndex> for FactPerspectivePrior {
582 fn from(value: MemFactIndex) -> Self {
583 Self::FactIndex(value)
584 }
585}
586
587impl From<Option<MemFactIndex>> for FactPerspectivePrior {
588 fn from(value: Option<MemFactIndex>) -> Self {
589 value.map_or(Self::None, Self::FactIndex)
590 }
591}
592
593impl From<MemFactPerspective> for FactPerspectivePrior {
594 fn from(value: MemFactPerspective) -> Self {
595 Self::FactPerspective(Box::new(value))
596 }
597}
598
599#[derive(Debug)]
600pub struct MemFactPerspective {
601 map: NamedFactMap,
602 prior: FactPerspectivePrior,
603}
604
605impl MemFactPerspective {
606 fn new(prior_facts: FactPerspectivePrior) -> MemFactPerspective {
607 Self {
608 map: NamedFactMap::new(),
609 prior: prior_facts,
610 }
611 }
612
613 fn clear(&mut self) {
614 self.map.clear();
615 }
616
617 fn apply_updates(&mut self, updates: &[Update]) {
618 for (name, key, value) in updates {
619 self.map
620 .entry(name.clone())
621 .or_default()
622 .insert(key.clone(), value.clone());
623 }
624 }
625}
626
627impl MemPerspective {
628 fn new(
629 prior: Prior<Location>,
630 parents: Prior<Address>,
631 policy: PolicyId,
632 prior_facts: FactPerspectivePrior,
633 max_cut: usize,
634 ) -> Self {
635 Self {
636 prior,
637 parents,
638 policy,
639 facts: MemFactPerspective::new(prior_facts),
640 commands: Vec::new(),
641 current_updates: Vec::new(),
642 max_cut,
643 }
644 }
645
646 fn new_unrooted(policy: PolicyId) -> Self {
647 Self {
648 prior: Prior::None,
649 parents: Prior::None,
650 policy,
651 facts: MemFactPerspective::new(FactPerspectivePrior::None),
652 commands: Vec::new(),
653 current_updates: Vec::new(),
654 max_cut: 0,
655 }
656 }
657}
658
659impl Revertable for MemPerspective {
660 fn checkpoint(&self) -> Checkpoint {
661 Checkpoint {
662 index: self.commands.len(),
663 }
664 }
665
666 fn revert(&mut self, checkpoint: Checkpoint) -> Result<(), Bug> {
667 if checkpoint.index == self.commands.len() {
668 return Ok(());
669 }
670
671 if checkpoint.index > self.commands.len() {
672 bug!("A checkpoint's index should always be less than or equal to the length of a perspective's command history!");
673 }
674
675 self.commands.truncate(checkpoint.index);
676 self.facts.clear();
677 self.current_updates.clear();
678 for data in &self.commands {
679 self.facts.apply_updates(&data.updates);
680 }
681
682 Ok(())
683 }
684}
685
686impl Perspective for MemPerspective {
687 fn add_command(&mut self, command: &impl Command) -> Result<usize, StorageError> {
688 if command.parent() != self.head_address()? {
689 return Err(StorageError::PerspectiveHeadMismatch);
690 }
691
692 let entry = CommandData {
693 command: MemCommand::from_cmd(command, self.head_address()?.next_max_cut()?),
694 updates: core::mem::take(&mut self.current_updates),
695 };
696 self.commands.push(entry);
697 Ok(self.commands.len())
698 }
699
700 fn policy(&self) -> PolicyId {
701 self.policy
702 }
703
704 fn includes(&self, id: CommandId) -> bool {
705 self.commands.iter().any(|cmd| cmd.command.id == id)
706 }
707
708 fn head_address(&self) -> Result<Prior<Address>, Bug> {
709 Ok(if let Some(last) = self.commands.last() {
710 Prior::Single(last.command.address()?)
711 } else {
712 self.parents
713 })
714 }
715}
716
717impl FactPerspective for MemPerspective {}
718
719impl Query for MemPerspective {
720 fn query(&self, name: &str, keys: &[Box<[u8]>]) -> Result<Option<Box<[u8]>>, StorageError> {
721 self.facts.query(name, keys)
722 }
723
724 type QueryIterator = <MemFactPerspective as Query>::QueryIterator;
725 fn query_prefix(
726 &self,
727 name: &str,
728 prefix: &[Box<[u8]>],
729 ) -> Result<Self::QueryIterator, StorageError> {
730 self.facts.query_prefix(name, prefix)
731 }
732}
733
734impl QueryMut for MemPerspective {
735 fn insert(&mut self, name: String, keys: Keys, value: Box<[u8]>) {
736 self.facts.insert(name.clone(), keys.clone(), value.clone());
737 self.current_updates.push((name, keys, Some(value)));
738 }
739
740 fn delete(&mut self, name: String, keys: Keys) {
741 self.facts.delete(name.clone(), keys.clone());
742 self.current_updates.push((name, keys, None));
743 }
744}
745
746impl MemFactPerspective {
747 fn query_prefix_inner(&self, name: &str, prefix: &[Box<[u8]>]) -> FactMap {
748 let map = self.map.get(name);
749 let mut matches = match &self.prior {
750 FactPerspectivePrior::None => BTreeMap::new(),
751 FactPerspectivePrior::FactPerspective(fp) => fp.query_prefix_inner(name, prefix),
752 FactPerspectivePrior::FactIndex(fi) => fi.query_prefix_inner(name, prefix),
753 };
754 if let Some(map) = map {
755 for (k, v) in find_prefixes(map, prefix) {
756 matches.insert(k.clone(), v.map(Into::into));
758 }
759 }
760 matches
761 }
762}
763
764impl FactPerspective for MemFactPerspective {}
765
766impl Query for MemFactPerspective {
767 fn query(&self, name: &str, keys: &[Box<[u8]>]) -> Result<Option<Box<[u8]>>, StorageError> {
768 if let Some(wrapped) = self.map.get(name).and_then(|m| m.get(keys)) {
769 return Ok(wrapped.as_deref().map(Box::from));
770 }
771 match &self.prior {
772 FactPerspectivePrior::None => Ok(None),
773 FactPerspectivePrior::FactPerspective(prior) => prior.query(name, keys),
774 FactPerspectivePrior::FactIndex(prior) => prior.query(name, keys),
775 }
776 }
777
778 type QueryIterator = Box<dyn Iterator<Item = Result<Fact, StorageError>>>;
779 fn query_prefix(
780 &self,
781 name: &str,
782 prefix: &[Box<[u8]>],
783 ) -> Result<Self::QueryIterator, StorageError> {
784 Ok(Box::from(
785 self.query_prefix_inner(name, prefix)
786 .into_iter()
787 .filter_map(|(key, value)| Some(Ok(Fact { key, value: value? }))),
789 ))
790 }
791}
792
793impl QueryMut for MemFactPerspective {
794 fn insert(&mut self, name: String, keys: Keys, value: Box<[u8]>) {
795 self.map.entry(name).or_default().insert(keys, Some(value));
796 }
797
798 fn delete(&mut self, name: String, keys: Keys) {
799 self.map.entry(name).or_default().insert(keys, None);
800 }
801}
802
803#[cfg(all(test, feature = "graphviz"))]
804pub mod graphviz {
805 #![allow(clippy::unwrap_used)]
806
807 use std::{fs::File, io::BufWriter};
808
809 use dot_writer::{Attributes, DotWriter, Style};
810
811 #[allow(clippy::wildcard_imports)]
812 use super::*;
813
814 fn loc(location: impl Into<Location>) -> String {
815 let location = location.into();
816 format!("\"{}:{}\"", location.segment, location.command)
817 }
818
819 fn get_seq(p: &MemFactIndex) -> &str {
820 if let Some(Some(seq)) = p.map.get("seq").and_then(|m| m.get(&Keys::default())) {
821 std::str::from_utf8(seq).unwrap()
822 } else {
823 ""
824 }
825 }
826
827 fn dotwrite(storage: &MemStorage, out: &mut DotWriter<'_>) {
828 let mut graph = out.digraph();
829 graph
830 .graph_attributes()
831 .set("compound", "true", false)
832 .set("rankdir", "RL", false)
833 .set_style(Style::Filled)
834 .set("color", "grey", false);
835 graph
836 .node_attributes()
837 .set("shape", "square", false)
838 .set_style(Style::Filled)
839 .set("color", "lightgrey", false);
840
841 let mut seen_facts = std::collections::HashMap::new();
842 let mut external_facts = Vec::new();
843
844 for segment in &storage.segments {
845 let mut cluster = graph.cluster();
846 match segment.prior {
847 Prior::None => {
848 cluster.graph_attributes().set("color", "green", false);
849 }
850 Prior::Single(..) => {}
851 Prior::Merge(..) => {
852 cluster.graph_attributes().set("color", "crimson", false);
853 }
854 }
855
856 for (i, cmd) in segment.commands.iter().enumerate() {
858 {
859 let mut node = cluster.node_named(loc((segment.index, i)));
860 node.set_label(&cmd.command.id.short_b58());
861 match cmd.command.parent {
862 Prior::None => {
863 node.set("shape", "house", false);
864 }
865 Prior::Single(..) => {}
866 Prior::Merge(..) => {
867 node.set("shape", "hexagon", false);
868 }
869 };
870 }
871 if i > 0 {
872 let previous = i.checked_sub(1).expect("i must be > 0");
873 cluster.edge(loc((segment.index, i)), loc((segment.index, previous)));
874 }
875 }
876
877 let first = loc(segment.first_location());
879 for p in segment.prior() {
880 cluster.edge(&first, loc(p));
881 }
882
883 let curr = segment.facts.name();
885 cluster
886 .node_named(curr.clone())
887 .set_label(get_seq(&segment.facts))
888 .set("shape", "cylinder", false)
889 .set("color", "black", false)
890 .set("style", "solid", false);
891 cluster
892 .edge(loc(segment.head_location()), &curr)
893 .attributes()
894 .set("color", "red", false);
895
896 seen_facts.insert(curr, segment.facts.clone());
897 let mut node = &segment.facts;
899 while let Some(prior) = &node.prior {
900 node = prior;
901 let name = node.name();
902 if seen_facts.insert(name, node.clone()).is_some() {
903 break;
904 }
905 external_facts.push(node.clone());
906 }
907 }
908
909 graph
910 .node_attributes()
911 .set("shape", "cylinder", false)
912 .set("color", "black", false)
913 .set("style", "solid", false);
914
915 for fact in external_facts {
916 graph.node_named(fact.name()).set_label(get_seq(&fact));
918
919 if let Some(prior) = &fact.prior {
921 graph
922 .edge(fact.name(), prior.name())
923 .attributes()
924 .set("color", "blue", false);
925 }
926 }
927
928 for segment in &storage.segments {
930 if let Some(prior) = &segment.facts.prior {
931 graph
932 .edge(segment.facts.name(), prior.name())
933 .attributes()
934 .set("color", "blue", false);
935 }
936 }
937
938 graph.node_named("HEAD").set("shape", "none", false);
940 graph.edge("HEAD", loc(storage.get_head().unwrap()));
941 }
942
943 pub fn dot(storage: &MemStorage, name: &str) {
944 std::fs::create_dir_all(".ignore").unwrap();
945 dotwrite(
946 storage,
947 &mut DotWriter::from(&mut BufWriter::new(
948 File::create(format!(".ignore/{name}.dot")).unwrap(),
949 )),
950 );
951 }
952}
953
954#[cfg(test)]
955mod test {
956 use super::*;
957 use crate::testing::dsl::{test_suite, StorageBackend};
958
959 #[test]
960 fn test_query_prefix() {
961 let mut graph = MemStorage::new();
962 let mut fp = MemFactPerspective::new(FactPerspectivePrior::None);
963
964 let name = "x";
965
966 let keys: &[&[&str]] = &[
967 &["aa", "xy", "123"],
968 &["aa", "xz", "123"],
969 &["bb", "ccc"],
970 &["bc", ""],
971 ];
972 let keys: Vec<Keys> = keys
973 .iter()
974 .map(|ks| ks.iter().map(|k| k.as_bytes()).collect())
975 .collect();
976
977 for ks in &keys {
978 fp.insert(
979 name.into(),
980 ks.clone(),
981 format!("{ks:?}").into_bytes().into(),
982 );
983 }
984 let facts = graph.write_facts(fp).unwrap();
985
986 let prefixes: &[&[&str]] = &[
987 &["aa", "xy", "12"],
988 &["aa", "xy"],
989 &["aa", "xz"],
990 &["aa", "x"],
991 &["bb", ""],
992 &["bb", "ccc"],
993 &["bc", ""],
994 &["bc", "", ""],
995 ];
996
997 for prefix in prefixes {
998 let prefix: Keys = prefix.iter().map(|k| k.as_bytes()).collect();
999 let found: Vec<_> = facts.query_prefix(name, &prefix).unwrap().collect();
1000 let mut expected: Vec<_> = keys.iter().filter(|k| k.starts_with(&prefix)).collect();
1001 expected.sort();
1002 assert_eq!(found.len(), expected.len());
1003 for (a, b) in std::iter::zip(found, expected) {
1004 let a = a.unwrap();
1005 assert_eq!(&a.key, b);
1006 assert_eq!(a.value.as_ref(), format!("{b:?}").as_bytes());
1007 }
1008 }
1009 }
1010
1011 struct MemBackend;
1012 impl StorageBackend for MemBackend {
1013 type StorageProvider = MemStorageProvider;
1014
1015 fn provider(&mut self, _client_id: u64) -> Self::StorageProvider {
1016 MemStorageProvider::new()
1017 }
1018 }
1019 test_suite!(|| MemBackend);
1020}