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