vortex_edition/
session.rs1use std::any::Any;
7use std::collections::BTreeMap;
8use std::sync::Arc;
9
10use parking_lot::RwLock;
11use vortex_session::ArcSwapMap;
12use vortex_session::SessionExt;
13use vortex_session::SessionGuard;
14use vortex_session::SessionVar;
15use vortex_session::registry::Id;
16
17use crate::Edition;
18use crate::EditionDeclaration;
19use crate::EditionError;
20use crate::EditionId;
21use crate::EditionInclusion;
22use crate::parse_release;
23
24#[derive(Clone, Debug, Default)]
32pub struct EditionSession {
33 inner: Arc<RwLock<Inner>>,
34}
35
36#[derive(Debug, Default)]
37struct Inner {
38 editions: BTreeMap<String, Edition>,
40 inclusions: BTreeMap<Id, EditionInclusion>,
42}
43
44type EditionsByFamily = ArcSwapMap<Id, EditionId>;
46
47#[derive(Clone, Debug, Default)]
57pub struct EnabledEditions {
58 inner: EditionsByFamily,
59}
60
61impl EnabledEditions {
62 pub fn editions(&self) -> Vec<EditionId> {
64 self.inner.read(|map| map.values().copied().collect())
65 }
66
67 fn enable(&self, edition: EditionId) {
68 self.inner.insert(Id::from(edition.family), edition);
71 }
72}
73
74impl EditionSession {
75 pub fn empty() -> Self {
77 Self {
78 inner: Arc::new(RwLock::new(Inner::default())),
79 }
80 }
81
82 pub fn declare(&self, declaration: &EditionDeclaration) -> Result<(), EditionError> {
86 self.declare_edition(declaration.edition)?;
87 for encoding in declaration.added {
88 self.declare_inclusion(EditionInclusion::new(*encoding, declaration.edition.id))?;
89 }
90 Ok(())
91 }
92
93 pub fn declare_edition(&self, edition: Edition) -> Result<(), EditionError> {
95 let mut inner = self.inner.write();
96 let key = edition.id.to_string();
97 if inner.editions.contains_key(&key) {
98 return Err(EditionError::new(format!("duplicate edition {key}")));
99 }
100 inner.editions.insert(key, edition);
101 Ok(())
102 }
103
104 pub fn declare_inclusion(&self, inclusion: EditionInclusion) -> Result<(), EditionError> {
107 let mut inner = self.inner.write();
108 if inner.inclusions.contains_key(&inclusion.encoding_id) {
109 return Err(EditionError::new(format!(
110 "duplicate edition inclusion for encoding {}",
111 inclusion.encoding_id
112 )));
113 }
114 inner.inclusions.insert(inclusion.encoding_id, inclusion);
115 Ok(())
116 }
117
118 pub fn editions(&self) -> Vec<Edition> {
122 let mut editions: Vec<Edition> = self.inner.read().editions.values().copied().collect();
123 editions.sort_by_key(|e| (e.id.family, e.id.year, e.id.month, e.id.version));
124 editions
125 }
126
127 pub fn find(&self, id: &EditionId) -> Option<Edition> {
129 self.inner.read().editions.get(&id.to_string()).copied()
130 }
131
132 pub fn current(&self, family: &str) -> Option<Edition> {
134 self.editions()
135 .into_iter()
136 .filter(|e| e.id.family == family && !e.is_draft())
137 .next_back()
138 }
139
140 pub fn encodings_in(&self, edition: &EditionId) -> Vec<EditionInclusion> {
143 self.inner
145 .read()
146 .inclusions
147 .values()
148 .filter(|inclusion| inclusion.since.is_at_or_before(edition))
149 .copied()
150 .collect()
151 }
152
153 pub fn validate(&self) -> Result<(), EditionError> {
158 let editions = self.editions();
159
160 for edition in &editions {
161 edition.id.validate()?;
162 if let Some(version) = edition.min_vortex_version
163 && parse_release(version).is_none()
164 {
165 return Err(EditionError::new(format!(
166 "edition {} declares malformed min_vortex_version {version:?}",
167 edition.id
168 )));
169 }
170 }
171
172 for pair in editions.windows(2) {
175 let (prev, next) = (&pair[0], &pair[1]);
176 if prev.id.family == next.id.family && prev.is_draft() && !next.is_draft() {
177 return Err(EditionError::new(format!(
178 "frozen edition {} follows draft {}; drafts must be newest in a family",
179 next.id, prev.id,
180 )));
181 }
182 }
183
184 let inner = self.inner.read();
185 for inclusion in inner.inclusions.values() {
186 inclusion.validate()?;
187
188 let Some(edition) = inner.editions.get(&inclusion.since.to_string()) else {
189 return Err(EditionError::new(format!(
190 "encoding {} is included in undeclared edition {}",
191 inclusion.encoding_id, inclusion.since
192 )));
193 };
194
195 if let Some(required) = inclusion.required_vortex_release.and_then(parse_release)
196 && let Some(declared) = edition.min_vortex_version.and_then(parse_release)
197 && required > declared
198 {
199 return Err(EditionError::new(format!(
200 "encoding {} requires release {}, newer than edition {}'s declared \
201 min_vortex_version",
202 inclusion.encoding_id,
203 inclusion.required_vortex_release.unwrap_or_default(),
204 edition.id,
205 )));
206 }
207 }
208
209 Ok(())
210 }
211}
212
213impl SessionVar for EditionSession {
214 fn as_any(&self) -> &dyn Any {
215 self
216 }
217
218 fn as_any_mut(&mut self) -> &mut dyn Any {
219 self
220 }
221}
222
223impl SessionVar for EnabledEditions {
224 fn as_any(&self) -> &dyn Any {
225 self
226 }
227
228 fn as_any_mut(&mut self) -> &mut dyn Any {
229 self
230 }
231}
232
233pub trait EditionSessionExt: SessionExt {
235 fn editions(&self) -> SessionGuard<'_, EditionSession> {
237 self.get::<EditionSession>()
238 }
239
240 fn enabled_editions(&self) -> SessionGuard<'_, EnabledEditions> {
245 self.get::<EnabledEditions>()
246 }
247
248 fn register_edition(&self, declaration: &EditionDeclaration) -> Result<(), EditionError> {
250 self.editions().declare(declaration)
251 }
252
253 fn enable_edition(&self, edition: EditionId) -> Result<(), EditionError> {
259 if self.editions().find(&edition).is_none() {
260 return Err(EditionError::new(format!(
261 "cannot enable unregistered edition {edition}"
262 )));
263 }
264 self.enabled_editions().enable(edition);
265 Ok(())
266 }
267
268 fn enabled_encoding_ids(&self) -> Vec<Id> {
273 let Some(enabled) = self.get_opt::<EnabledEditions>() else {
274 return vec![];
275 };
276 let editions = self.editions();
277 let mut ids: Vec<Id> = enabled
278 .editions()
279 .iter()
280 .flat_map(|edition| editions.encodings_in(edition))
281 .map(|inclusion| inclusion.encoding_id)
282 .collect();
283 ids.sort_unstable();
284 ids.dedup();
285 ids
286 }
287}
288
289impl<S: SessionExt> EditionSessionExt for S {}