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::{ChangeObserver, 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    /// [`Self::update`], reporting every change the decorator stack made.
126    ///
127    /// See [`LinkStorage::update_observed`](crate::LinkStorage::update_observed).
128    pub fn update_observed(
129        &mut self,
130        id: u32,
131        source: u32,
132        target: u32,
133        observer: ChangeObserver<'_>,
134    ) -> Result<Link> {
135        self.links.update_observed(id, source, target, observer)
136    }
137
138    pub fn delete(&mut self, id: u32) -> Result<Link> {
139        self.links.delete(id)
140    }
141
142    /// [`Self::delete`], reporting every change the decorator stack made.
143    ///
144    /// See [`LinkStorage::delete_observed`](crate::LinkStorage::delete_observed).
145    pub fn delete_observed(&mut self, id: u32, observer: ChangeObserver<'_>) -> Result<Link> {
146        self.links.delete_observed(id, observer)
147    }
148
149    pub fn all(&self) -> Vec<&Link> {
150        self.links.all()
151    }
152
153    pub fn query(
154        &self,
155        index: Option<u32>,
156        source: Option<u32>,
157        target: Option<u32>,
158    ) -> Vec<&Link> {
159        self.links.query(index, source, target)
160    }
161
162    pub fn search(&self, source: u32, target: u32) -> Option<u32> {
163        self.links.search(source, target)
164    }
165
166    pub fn get_or_create(&mut self, source: u32, target: u32) -> u32 {
167        self.links.get_or_create(source, target)
168    }
169}
170
171impl PinnedTypesAccess for PinnedTypesDecorator {
172    fn pinned_types(&mut self, count: usize) -> Result<Vec<u32>> {
173        PinnedTypes::new(&mut self.links).take_types(count)
174    }
175}