Skip to main content

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}