vortex_edition/
session.rs1use std::any::Any;
7use std::collections::BTreeMap;
8use std::sync::Arc;
9
10use parking_lot::RwLock;
11use vortex_error::VortexResult;
12use vortex_error::vortex_bail;
13use vortex_session::ArcSwapMap;
14use vortex_session::SessionExt;
15use vortex_session::SessionGuard;
16use vortex_session::SessionVar;
17use vortex_session::registry::Id;
18
19use crate::ComponentKind;
20use crate::Edition;
21use crate::EditionDeclaration;
22use crate::EditionFamily;
23use crate::EditionId;
24use crate::EditionInclusion;
25use crate::parse_release;
26
27#[derive(Clone, Debug, Default)]
35pub struct EditionSession {
36 inner: Arc<RwLock<Inner>>,
37}
38
39#[derive(Debug, Default)]
40struct Inner {
41 families: BTreeMap<String, EditionFamily>,
43 editions: BTreeMap<String, Edition>,
45 inclusions: BTreeMap<ComponentKind, BTreeMap<Id, Vec<EditionInclusion>>>,
49}
50
51type EditionsByFamily = ArcSwapMap<Id, EditionId>;
53
54#[derive(Clone, Debug, Default)]
64pub struct EnabledEditions {
65 inner: EditionsByFamily,
66}
67
68impl EnabledEditions {
69 pub fn editions(&self) -> Vec<EditionId> {
71 self.inner.read(|map| map.values().copied().collect())
72 }
73
74 fn enable(&self, edition: EditionId) {
75 self.inner.insert(Id::from(edition.family), edition);
78 }
79}
80
81impl EditionSession {
82 pub fn empty() -> Self {
84 Self {
85 inner: Arc::new(RwLock::new(Inner::default())),
86 }
87 }
88
89 pub fn declare(&self, declaration: &EditionDeclaration) -> VortexResult<()> {
92 self.declare_edition(declaration.edition)?;
93 for member in declaration.added {
94 self.declare_inclusion(EditionInclusion::new(
95 member.kind,
96 member.component,
97 declaration.edition.id,
98 ))?;
99 }
100 Ok(())
101 }
102
103 pub fn declare_family(&self, family: &EditionFamily) -> VortexResult<()> {
107 let mut inner = self.inner.write();
108 if inner.families.contains_key(family.name) {
109 vortex_bail!("duplicate edition family {}", family.name);
110 }
111 inner.families.insert(family.name.to_string(), *family);
112 Ok(())
113 }
114
115 pub fn families(&self) -> Vec<EditionFamily> {
117 self.inner.read().families.values().copied().collect()
118 }
119
120 pub fn find_family(&self, name: &str) -> Option<EditionFamily> {
122 self.inner.read().families.get(name).copied()
123 }
124
125 pub fn declare_edition(&self, edition: Edition) -> VortexResult<()> {
127 let mut inner = self.inner.write();
128 let key = edition.id.to_string();
129 if inner.editions.contains_key(&key) {
130 vortex_bail!("duplicate edition {key}");
131 }
132 inner.editions.insert(key, edition);
133 Ok(())
134 }
135
136 pub fn declare_inclusion(&self, inclusion: EditionInclusion) -> VortexResult<()> {
140 let mut inner = self.inner.write();
141 let by_id = inner.inclusions.entry(inclusion.kind).or_default();
142 let history = by_id.entry(inclusion.component_id).or_default();
143 let previous = history
144 .iter()
145 .filter(|existing| existing.since.family == inclusion.since.family)
146 .max_by_key(|existing| {
147 (
148 existing.since.year,
149 existing.since.month,
150 existing.since.version,
151 )
152 });
153
154 if let Some(previous) = previous {
155 vortex_bail!(
156 "{} {} already joined family {} in edition {}",
157 inclusion.kind,
158 inclusion.component_id,
159 inclusion.since.family,
160 previous.since,
161 );
162 }
163 history.push(inclusion);
164 history.sort_by_key(|entry| {
165 (
166 entry.since.family,
167 entry.since.year,
168 entry.since.month,
169 entry.since.version,
170 )
171 });
172 Ok(())
173 }
174
175 pub fn editions(&self) -> Vec<Edition> {
179 let mut editions: Vec<Edition> = self.inner.read().editions.values().copied().collect();
180 editions.sort_by_key(|e| (e.id.family, e.id.year, e.id.month, e.id.version));
181 editions
182 }
183
184 pub fn find(&self, id: &EditionId) -> Option<Edition> {
186 self.inner.read().editions.get(&id.to_string()).copied()
187 }
188
189 pub fn current(&self, family: &str) -> Option<Edition> {
191 self.editions()
192 .into_iter()
193 .rfind(|e| e.id.family == family && !e.is_draft())
194 }
195
196 pub fn components_in(&self, edition: &EditionId, kind: ComponentKind) -> Vec<EditionInclusion> {
200 let inner = self.inner.read();
201 let Some(by_id) = inner.inclusions.get(&kind) else {
202 return vec![];
203 };
204 by_id
205 .values()
206 .filter_map(|history| {
207 history
208 .iter()
209 .filter(|inclusion| inclusion.since.is_at_or_before(edition))
210 .max_by_key(|inclusion| {
211 (
212 inclusion.since.year,
213 inclusion.since.month,
214 inclusion.since.version,
215 )
216 })
217 .copied()
218 })
219 .collect()
220 }
221
222 pub fn validate(&self) -> VortexResult<()> {
227 let editions = self.editions();
228
229 for family in self.families() {
230 family.validate()?;
231 }
232
233 for edition in &editions {
234 edition.id.validate()?;
235 if self.find_family(edition.id.family).is_none() {
236 vortex_bail!(
237 "edition {} belongs to undeclared family {}; declare the family before \
238 its editions",
239 edition.id,
240 edition.id.family,
241 );
242 }
243 if let Some(version) = edition.min_library_version
244 && parse_release(version).is_none()
245 {
246 vortex_bail!(
247 "edition {} declares malformed min_library_version {version:?}",
248 edition.id
249 );
250 }
251 }
252
253 for pair in editions.windows(2) {
256 let (prev, next) = (&pair[0], &pair[1]);
257 if prev.id.family == next.id.family && prev.is_draft() && !next.is_draft() {
258 vortex_bail!(
259 "frozen edition {} follows draft {}; drafts must be newest in a family",
260 next.id,
261 prev.id,
262 );
263 }
264 }
265
266 let inner = self.inner.read();
267 for inclusion in inner
268 .inclusions
269 .values()
270 .flat_map(|by_id| by_id.values())
271 .flatten()
272 {
273 inclusion.validate()?;
274
275 let Some(edition) = inner.editions.get(&inclusion.since.to_string()) else {
276 vortex_bail!(
277 "{} {} is included in undeclared edition {}",
278 inclusion.kind,
279 inclusion.component_id,
280 inclusion.since
281 );
282 };
283
284 if let Some(required) = inclusion.required_vortex_release.and_then(parse_release)
285 && let Some(declared) = edition.min_library_version.and_then(parse_release)
286 && required > declared
287 {
288 vortex_bail!(
289 "{} {} requires release {}, newer than edition {}'s declared \
290 min_library_version",
291 inclusion.kind,
292 inclusion.component_id,
293 inclusion.required_vortex_release.unwrap_or_default(),
294 edition.id,
295 );
296 }
297 }
298
299 Ok(())
300 }
301}
302
303impl SessionVar for EditionSession {
304 fn as_any(&self) -> &dyn Any {
305 self
306 }
307
308 fn as_any_mut(&mut self) -> &mut dyn Any {
309 self
310 }
311}
312
313impl SessionVar for EnabledEditions {
314 fn as_any(&self) -> &dyn Any {
315 self
316 }
317
318 fn as_any_mut(&mut self) -> &mut dyn Any {
319 self
320 }
321}
322
323pub trait EditionSessionExt: SessionExt {
325 fn editions(&self) -> SessionGuard<'_, EditionSession> {
327 self.get::<EditionSession>()
328 }
329
330 fn enabled_editions(&self) -> SessionGuard<'_, EnabledEditions> {
335 self.get::<EnabledEditions>()
336 }
337
338 fn register_edition(&self, declaration: &EditionDeclaration) -> VortexResult<()> {
340 self.editions().declare(declaration)
341 }
342
343 fn enable_edition(&self, edition: EditionId) -> VortexResult<()> {
349 if self.editions().find(&edition).is_none() {
350 vortex_bail!("cannot enable unregistered edition {edition}");
351 }
352 self.enabled_editions().enable(edition);
353 Ok(())
354 }
355
356 fn enabled_component_ids(&self, kind: ComponentKind) -> Vec<Id> {
362 let Some(enabled) = self.get_opt::<EnabledEditions>() else {
363 return vec![];
364 };
365 let editions = self.editions();
366 let mut ids: Vec<Id> = enabled
367 .editions()
368 .iter()
369 .flat_map(|edition| editions.components_in(edition, kind))
370 .map(|inclusion| inclusion.component_id)
371 .collect();
372 ids.sort_unstable();
373 ids.dedup();
374 ids
375 }
376}
377
378impl<S: SessionExt> EditionSessionExt for S {}