aranya_runtime/storage/
memory.rs

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    // Ensure the segments are dropped high to low, which helps avoid a stack
177    // overflow on dropping really long Arc chains.
178    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        // TODO(jdygert): ensure braid belongs to this storage.
254        // TODO(jdygert): ensure braid ends at given command?
255
256        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        // Add the commands to the segment
309        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        // TODO(jdygert): ensure segment belongs to self?
342
343        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                // remove deleted facts
409                .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        // walk backwards along fact indices
420        while let Some(facts) = prior {
421            if let Some(map) = facts.map.get(name) {
422                for (k, v) in find_prefixes(map, prefix) {
423                    // don't override, if we've already found the fact (including deletions)
424                    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                // overwrite "earlier" facts
754                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                // remove deleted facts
785                .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            // Draw commands and edges between commands within the segment.
855            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            // Draw edges to previous segments.
876            let first = loc(segment.first_location());
877            for p in segment.prior() {
878                cluster.edge(&first, loc(p));
879            }
880
881            // Draw fact index for this segment.
882            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            // Make sure prior facts of fact index will get processed later.
896            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            // Draw nodes for fact indices not directly associated with a segment.
915            graph.node_named(fact.name()).set_label(get_seq(&fact));
916
917            // Draw edge to prior facts.
918            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        // Draw edges to prior facts for fact indices in segments.
927        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        // Draw HEAD indicator.
937        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}