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