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