aranya_runtime/storage/
memory.rs

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    // Ensure the segments are dropped high to low, which helps avoid a stack
163    // overflow on dropping really long Arc chains.
164    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        // TODO(jdygert): ensure braid belongs to this storage.
251        // TODO(jdygert): ensure braid ends at given command?
252
253        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        // Add the commands to the segment
306        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        // TODO(jdygert): ensure segment belongs to self?
339
340        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                // remove deleted facts
406                .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        // walk backwards along fact indices
417        while let Some(facts) = prior {
418            if let Some(map) = facts.map.get(name) {
419                for (k, v) in find_prefixes(map, prefix) {
420                    // don't override, if we've already found the fact (including deletions)
421                    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                // overwrite "earlier" facts
751                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                // remove deleted facts
782                .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            // Draw commands and edges between commands within the segment.
851            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            // Draw edges to previous segments.
872            let first = loc(segment.first_location());
873            for p in segment.prior() {
874                cluster.edge(&first, loc(p));
875            }
876
877            // Draw fact index for this segment.
878            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            // Make sure prior facts of fact index will get processed later.
892            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            // Draw nodes for fact indices not directly associated with a segment.
911            graph.node_named(fact.name()).set_label(get_seq(&fact));
912
913            // Draw edge to prior facts.
914            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        // Draw edges to prior facts for fact indices in segments.
923        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        // Draw HEAD indicator.
933        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}