drizzle_migrations/collection.rs
1//! Shared generic entity collection for DDL storage.
2//!
3//! [`EntityCollection<T>`] is a thin `Vec<T>` wrapper that backs both the
4//! SQLite and Postgres DDL pipelines. The generic operations (push, list,
5//! is_empty, len, mutable access, ...) live here once; per-dialect `impl
6//! EntityCollection<DialectEntity>` blocks in `sqlite/collection.rs` and
7//! `postgres/collection.rs` add typed lookup helpers (`one(name)`,
8//! `for_table(table)`, etc.) whose shape depends on the dialect's entity
9//! identity (single-name vs (schema, name) vs (schema, table, name)).
10//!
11//! ## Why a Vec wrapper, not an indexed map
12//!
13//! The DDL serializer needs to emit entities in insertion order (the order
14//! the user declared them). A Vec preserves that for free; a HashMap would
15//! need a parallel ordering structure. Duplicate keys are also allowed
16//! during partial state — `push` always succeeds, callers de-duplicate
17//! explicitly when they need uniqueness.
18
19// =============================================================================
20// Entity Collection - Typed Operations
21// =============================================================================
22
23/// Generic DDL entity collection with typed operations.
24///
25/// See module docs for the design rationale. Per-dialect `impl
26/// EntityCollection<…>` blocks supplying entity-aware lookups live in
27/// `sqlite/collection.rs` and `postgres/collection.rs`.
28#[derive(Debug, Clone)]
29pub struct EntityCollection<T> {
30 /// Crate-private so per-dialect `impl EntityCollection<DialectEntity>`
31 /// blocks (in `sqlite/collection.rs` and `postgres/collection.rs`) can
32 /// supply entity-aware lookup helpers without going through accessor
33 /// methods. Not part of the public API.
34 pub(crate) entities: Vec<T>,
35}
36
37impl<T> Default for EntityCollection<T> {
38 fn default() -> Self {
39 Self {
40 entities: Vec::new(),
41 }
42 }
43}
44
45impl<T> EntityCollection<T> {
46 /// Create empty collection.
47 #[must_use]
48 pub const fn new() -> Self {
49 Self {
50 entities: Vec::new(),
51 }
52 }
53
54 /// Push an entity. Always succeeds — duplicate detection is the
55 /// caller's responsibility (see module docs).
56 pub fn push(&mut self, entity: T) {
57 self.entities.push(entity);
58 }
59
60 /// List all entities in insertion order.
61 #[must_use]
62 pub fn list(&self) -> &[T] {
63 &self.entities
64 }
65
66 /// Mutable access to the underlying `Vec`.
67 pub const fn list_mut(&mut self) -> &mut Vec<T> {
68 &mut self.entities
69 }
70
71 /// Check if empty.
72 #[must_use]
73 pub const fn is_empty(&self) -> bool {
74 self.entities.is_empty()
75 }
76
77 /// Number of entities currently in the collection.
78 #[must_use]
79 pub const fn len(&self) -> usize {
80 self.entities.len()
81 }
82}
83
84impl<T> Extend<T> for EntityCollection<T> {
85 fn extend<I>(&mut self, iter: I)
86 where
87 I: IntoIterator<Item = T>,
88 {
89 self.entities.extend(iter);
90 }
91}
92
93impl<T: Clone> EntityCollection<T> {
94 /// Consume the collection and return the underlying `Vec`.
95 #[must_use]
96 pub fn into_vec(self) -> Vec<T> {
97 self.entities
98 }
99
100 /// Update entities matching `predicate` with `transform`.
101 pub fn update_where<F, P>(&mut self, predicate: P, mut transform: F)
102 where
103 F: FnMut(&mut T),
104 P: Fn(&T) -> bool,
105 {
106 for entity in &mut self.entities {
107 if predicate(entity) {
108 transform(entity);
109 }
110 }
111 }
112
113 /// Update every entity with `transform`.
114 pub fn update_all<F>(&mut self, mut transform: F)
115 where
116 F: FnMut(&mut T),
117 {
118 for entity in &mut self.entities {
119 transform(entity);
120 }
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::EntityCollection;
127
128 #[test]
129 fn extend_preserves_insertion_order() {
130 let mut entities = EntityCollection::new();
131 entities.push(1);
132 entities.extend([2, 3]);
133
134 assert_eq!(entities.list(), &[1, 2, 3]);
135 }
136}