Skip to main content

link_cli/
pinned_types.rs

1//! Pinned type allocation compatible with the C# `PinnedTypes` helper.
2
3use anyhow::{bail, Result};
4use std::path::Path;
5
6use crate::link::Link;
7use crate::link_storage::LinkStorage;
8
9pub trait PinnedTypesAccess {
10    fn pinned_types(&mut self, count: usize) -> Result<Vec<u32>>;
11
12    fn deconstruct_pinned_types(&mut self) -> Result<(u32, u32, u32)> {
13        let pinned_types = self.pinned_types(3)?;
14        Ok((pinned_types[0], pinned_types[1], pinned_types[2]))
15    }
16}
17
18/// Creates or validates the reserved type links at deterministic addresses.
19pub struct PinnedTypes<'a> {
20    links: &'a mut LinkStorage,
21    current: u32,
22    initial_source: u32,
23}
24
25impl<'a> PinnedTypes<'a> {
26    pub fn new(links: &'a mut LinkStorage) -> Self {
27        Self {
28            links,
29            current: 1,
30            initial_source: 1,
31        }
32    }
33
34    pub fn next_type(&mut self) -> Result<u32> {
35        let address = self.current;
36        if let Some(link) = self.links.get(address) {
37            if link.index != address || link.source != self.initial_source || link.target != address
38            {
39                bail!(
40                    "Unexpected link found at address {address}. Expected: ({address}: {} {address}), Found: ({}: {} {}).",
41                    self.initial_source,
42                    link.index,
43                    link.source,
44                    link.target
45                );
46            }
47        } else {
48            let created = self.links.get_or_create(self.initial_source, address);
49            if created != address {
50                bail!(
51                    "Pinned type address {address} could not be created deterministically; got {created}."
52                );
53            }
54        }
55
56        self.current += 1;
57        Ok(address)
58    }
59
60    pub fn take_types(&mut self, count: usize) -> Result<Vec<u32>> {
61        (0..count).map(|_| self.next_type()).collect()
62    }
63
64    pub fn deconstruct(&mut self) -> Result<(u32, u32, u32)> {
65        let pinned_types = self.take_types(3)?;
66        Ok((pinned_types[0], pinned_types[1], pinned_types[2]))
67    }
68}
69
70/// Link storage decorator that exposes pinned type allocation.
71pub struct PinnedTypesDecorator {
72    links: LinkStorage,
73}
74
75impl PinnedTypesDecorator {
76    pub fn new<P>(database_filename: P, trace: bool) -> Result<Self>
77    where
78        P: AsRef<Path>,
79    {
80        let database_path = database_filename.as_ref().to_string_lossy().into_owned();
81        let links = LinkStorage::new(&database_path, trace)?;
82        Ok(Self::from_link_storage(links))
83    }
84
85    pub fn from_link_storage(links: LinkStorage) -> Self {
86        Self { links }
87    }
88
89    pub fn links(&self) -> &LinkStorage {
90        &self.links
91    }
92
93    pub fn links_mut(&mut self) -> &mut LinkStorage {
94        &mut self.links
95    }
96
97    pub fn into_link_storage(self) -> LinkStorage {
98        self.links
99    }
100
101    pub fn save(&self) -> Result<()> {
102        self.links.save()
103    }
104
105    pub fn create(&mut self, source: u32, target: u32) -> u32 {
106        self.links.create(source, target)
107    }
108
109    pub fn ensure_created(&mut self, id: u32) -> u32 {
110        self.links.ensure_created(id)
111    }
112
113    pub fn get(&self, id: u32) -> Option<&Link> {
114        self.links.get(id)
115    }
116
117    pub fn exists(&self, id: u32) -> bool {
118        self.links.exists(id)
119    }
120
121    pub fn update(&mut self, id: u32, source: u32, target: u32) -> Result<Link> {
122        self.links.update(id, source, target)
123    }
124
125    pub fn delete(&mut self, id: u32) -> Result<Link> {
126        self.links.delete(id)
127    }
128
129    pub fn all(&self) -> Vec<&Link> {
130        self.links.all()
131    }
132
133    pub fn query(
134        &self,
135        index: Option<u32>,
136        source: Option<u32>,
137        target: Option<u32>,
138    ) -> Vec<&Link> {
139        self.links.query(index, source, target)
140    }
141
142    pub fn search(&self, source: u32, target: u32) -> Option<u32> {
143        self.links.search(source, target)
144    }
145
146    pub fn get_or_create(&mut self, source: u32, target: u32) -> u32 {
147        self.links.get_or_create(source, target)
148    }
149}
150
151impl PinnedTypesAccess for PinnedTypesDecorator {
152    fn pinned_types(&mut self, count: usize) -> Result<Vec<u32>> {
153        PinnedTypes::new(&mut self.links).take_types(count)
154    }
155}