snaplog/scoped/mod.rs
1//! A scoped [`ScopedSnaplog`][Snaplog] and it's associated types. A `ScopedSnaplog` is used to
2//! record snapshots of changes to only part of a value, such as successive edits to a file's name
3//! without editing it's ancestors.
4//!
5//! To scope a type, implement [`IntoScoped`], a trait to denote how to deconstruct and reconstruct
6//! the type, the `ScopedSnaplog` takes care of the rest.
7//!
8//! # Examples
9//! All examples in this module assume this type is given.
10//!
11//! Given a type like this:
12//! ```
13//! use snaplog::scoped::IntoScoped;
14//!
15//! #[derive(Debug, PartialEq)]
16//! pub struct Prefixed {
17//! pub prefix: Option<&'static str>,
18//! pub content: &'static str,
19//! }
20//!
21//! impl Prefixed {
22//! pub fn new(s: &'static str) -> Self {
23//! let parts = s.split_once(':');
24//!
25//! Self {
26//! prefix: parts.map(|(p, _)| p),
27//! content: parts.map(|(_, c)| c).unwrap_or(s),
28//! }
29//! }
30//! }
31//!
32//! impl IntoScoped for Prefixed {
33//! type Scope = &'static str;
34//! type Ignored = Option<&'static str>;
35//!
36//! fn into_scoped(self) -> (Self::Scope, Self::Ignored) {
37//! (self.content, self.prefix)
38//! }
39//!
40//! fn from_scoped(scope: Self::Scope, ignored: Self::Ignored) -> Self {
41//! Self {
42//! prefix: ignored,
43//! content: scope,
44//! }
45//! }
46//! }
47//! ```
48//!
49//! You can use the [`Snaplog`] like this:
50//! ```
51//! # use snaplog::{Select, scoped::{Snaplog, __Prefixed as Prefixed}};
52//! let mut snaplog = Snaplog::new(Prefixed::new("prefix:content"));
53//! assert_eq!(snaplog.has_changes(), false);
54//!
55//! snaplog.record_change(|prev| { assert_eq!(prev, &"content"); "content-copy" });
56//! snaplog.record_change(|prev| { assert_eq!(prev, &"content-copy"); "new" });
57//! assert_eq!(snaplog.has_changes(), true);
58//!
59//! assert_eq!(snaplog[Select::Initial], "content");
60//! assert_eq!(snaplog[Select::At(1)], "content-copy");
61//! assert_eq!(snaplog[Select::Current], "new");
62//!
63//! snaplog.clear_history();
64//!
65//! assert_eq!(snaplog.history(), ["new"]);
66//! assert_eq!(snaplog.has_changes(), false);
67//! # Ok::<_, Box<dyn std::error::Error>>(())
68//! ```
69//!
70//! And when all changes are done you can simply recombine the parts:
71//! ```
72//! # use snaplog::{Select, scoped::{Snaplog, __Prefixed as Prefixed}};
73//! # let mut snaplog = Snaplog::new(Prefixed::new("prefix:content"));
74//! # snaplog.record("content-copy");
75//! # snaplog.record("new");
76//! assert_eq!(snaplog.into_current(), Prefixed::new("prefix:new"));
77//! ```
78
79use crate::full;
80use crate::{EmptyHistoryError, Select};
81
82use std::collections::TryReserveError;
83use std::ops::RangeBounds;
84
85mod docs_impl;
86
87#[doc(hidden)]
88pub use docs_impl::__Prefixed;
89
90/// A trait for types that can be scoped into parts to apply changes only partially. See
91/// [`ScopedSnaplog`][Snaplog] for examples.
92pub trait IntoScoped {
93 /// The type of the scope that is used when applying changes.
94 type Scope;
95
96 /// The type of the part that is ignored when applying changes.
97 type Ignored;
98
99 /// Separates `Self` into it's scoped and ignored part.
100 fn into_scoped(self) -> (Self::Scope, Self::Ignored);
101
102 /// Creates `Self` from it's scope and ignored part.
103 fn from_scoped(scope: Self::Scope, ignored: Self::Ignored) -> Self;
104}
105
106/// A trait for [`T: IntoScoped`][IntoScoped] that can create thin immutable reference wrappers from
107/// references to their parts which behave like a reference to `Self`.
108///
109/// This trait has correctness requirements similar to [`Borrow`][0], if a trait like [`Eq`],
110/// [`Ord`] or [`Hash`] is implemented for `T`, then the same conditions formulated by `Borrow` must
111/// hold.
112///
113/// [0]: std::borrow::Borrow
114pub trait AsThinScoped: IntoScoped {
115 /// An immutable thin reference type that behaves like `&Self`.
116 type ThinScoped<'this>;
117
118 /// Creates [`Self::ThinScoped`] from references to it's scope and ignored part.
119 fn as_thin_scoped<'a>(
120 scope: &'a Self::Scope,
121 ignored: &'a Self::Ignored,
122 ) -> Self::ThinScoped<'a>;
123}
124
125/// A trait for [`T: IntoScoped`][IntoScoped] that can create thin mutable reference wrappers from
126/// mutable references to their parts which behave like a reference to `Self`.
127///
128/// This trait has correctness requirements similar to [`BorrowMut`][0], if a trait like [`Eq`],
129/// [`Ord`] or [`Hash`] is implemented for `T`, then the same conditions formulated by `BorrowMut`
130/// must hold.
131///
132/// [0]: std::borrow::BorrowMut
133pub trait AsThinScopedMut: AsThinScoped {
134 /// A mutable thin reference type that behaves like `&mut Self`.
135 type ThinScopedMut<'this>;
136
137 /// Creates [`Self::ThinScopedMut`] from mutable references to it's scope and ignored part.
138 fn as_thin_scoped_mut<'a>(
139 scope: &'a mut Self::Scope,
140 ignored: &'a mut Self::Ignored,
141 ) -> Self::ThinScopedMut<'a>;
142}
143
144/// A [`Snaplog`][full] that is scoped to only part of of a type.
145///
146/// # Examples
147/// ```
148/// # use snaplog::{Select, scoped::{Snaplog, __Prefixed as Prefixed}};
149/// let mut snaplog = Snaplog::new(Prefixed::new("prefix:content"));
150/// assert_eq!(snaplog.has_changes(), false);
151///
152/// snaplog.record_change(|prev| { assert_eq!(prev, &"content"); "content-copy" });
153/// snaplog.record_change(|prev| { assert_eq!(prev, &"content-copy"); "new" });
154/// assert_eq!(snaplog.has_changes(), true);
155///
156/// assert_eq!(snaplog[Select::Initial], "content");
157/// assert_eq!(snaplog[Select::At(1)], "content-copy");
158/// assert_eq!(snaplog[Select::Current], "new");
159///
160/// snaplog.clear_history();
161///
162/// assert_eq!(snaplog.history(), ["new"]);
163/// assert_eq!(snaplog.has_changes(), false);
164/// # Ok::<_, Box<dyn std::error::Error>>(())
165/// ```
166///
167/// [full]: full::Snaplog
168#[derive(Debug, PartialEq, Eq)]
169pub struct Snaplog<T: IntoScoped> {
170 full: full::Snaplog<T::Scope>,
171 ignored: T::Ignored,
172}
173
174/// Various constructor functions.
175impl<T: IntoScoped> Snaplog<T> {
176 /// Creates a new [`Snaplog`] from the given `initial` snapshot with no recorded changes.
177 ///
178 /// # Examples
179 /// ```
180 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
181 /// let snaplog = Snaplog::new(Prefixed::new("prefix:content"));
182 ///
183 /// assert_eq!(snaplog.initial(), &"content");
184 /// assert_eq!(snaplog.ignored(), &Some("prefix"));
185 /// assert_eq!(snaplog.has_changes(), false);
186 /// ```
187 pub fn new(initial: T) -> Self {
188 let (scope, ignored) = initial.into_scoped();
189
190 Self {
191 full: full::Snaplog::new(scope),
192 ignored,
193 }
194 }
195
196 /// Creates a new [`Snaplog`] for the given `history` backing vector.
197 ///
198 /// # Errors
199 /// Returns an error if `history` was empty.
200 ///
201 /// # Examples
202 /// ```
203 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
204 /// assert!(Snaplog::<Prefixed>::try_from_vec(vec!["content"], None).is_ok());
205 /// assert!(Snaplog::<Prefixed>::try_from_vec(vec![], Some("prefix")).is_err());
206 /// ```
207 pub fn try_from_vec(
208 history: Vec<T::Scope>,
209 ignored: T::Ignored,
210 ) -> Result<Self, EmptyHistoryError> {
211 Ok(Self {
212 full: full::Snaplog::try_from_vec(history)?,
213 ignored,
214 })
215 }
216
217 /// Creates a new [`Snaplog`] for the given `history` backing vector.
218 ///
219 /// # Panics
220 /// Panics if `history` was empty.
221 ///
222 /// # Examples
223 /// ```
224 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
225 /// let snaplog: Snaplog<Prefixed> = Snaplog::from_vec(vec![""], None);
226 /// ```
227 ///
228 /// This panics:
229 /// ```should_panic
230 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
231 /// let snaplog: Snaplog<Prefixed> = Snaplog::from_vec(vec![], Some("Prefix"));
232 /// ```
233 pub fn from_vec(history: Vec<T::Scope>, ignored: T::Ignored) -> Self {
234 match Self::try_from_vec(history, ignored) {
235 Ok(this) => this,
236 Err(_) => panic!("history must not be empty"),
237 }
238 }
239
240 /// Creates a new [`Snaplog`] from the given `history`. The elements are collected into a
241 /// [`Vec`] the if you already have a vec at hand use [`from_vec`][Self::try_from_vec]. The
242 /// first element is used as the initial element.
243 ///
244 /// # Errors
245 /// Returns an error if `history` was empty.
246 ///
247 /// # Examples
248 /// ```
249 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
250 /// assert!(Snaplog::<Prefixed>::try_from_history(["a", "b", "c"], None).is_ok());
251 /// assert!(Snaplog::<Prefixed>::try_from_history(std::iter::empty(), Some("a")).is_err());
252 /// ```
253 pub fn try_from_history<I>(history: I, ignored: T::Ignored) -> Result<Self, EmptyHistoryError>
254 where
255 I: IntoIterator<Item = T::Scope>,
256 {
257 Self::try_from_vec(history.into_iter().collect(), ignored)
258 }
259
260 /// Creates a new [`Snaplog`] from the given `history`. The elements are collected into a
261 /// [`Vec`] the if you already have a vec at hand use [`from_vec`][Self::from_vec]. The first
262 /// element is used as the initial element.
263 ///
264 /// # Panics
265 /// Panics if `history` was empty.
266 ///
267 /// # Examples
268 /// ```
269 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
270 /// let snaplog: Snaplog<Prefixed> = Snaplog::from_history(["a", "b", "c"], None);
271 /// ```
272 ///
273 /// This panics:
274 /// ```should_panic
275 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
276 /// let snaplog: Snaplog<Prefixed> = Snaplog::from_history(std::iter::empty(), Some("prefix"));
277 /// ```
278 pub fn from_history<I>(history: I, ignored: T::Ignored) -> Self
279 where
280 I: IntoIterator<Item = T::Scope>,
281 {
282 Self::from_vec(history.into_iter().collect(), ignored)
283 }
284}
285
286/// First class [`Snaplog`] members.
287impl<T: IntoScoped> Snaplog<T> {
288 /// Returns a reference to the internal [`Snaplog`].
289 ///
290 /// # Examples
291 /// ```
292 /// # use snaplog::{full, scoped::{Snaplog, __Prefixed as Prefixed}};
293 /// let snaplog = Snaplog::new(Prefixed::new("prefix:content"));
294 ///
295 /// assert_eq!(snaplog.scope(), &full::Snaplog::new("content"));
296 /// ```
297 pub fn scope(&self) -> &full::Snaplog<T::Scope> {
298 &self.full
299 }
300
301 /// Returns a mutable reference to the internal [`full::Snaplog`].
302 ///
303 /// # Examples
304 /// ```
305 /// # use snaplog::{full, scoped::{Snaplog, __Prefixed as Prefixed}};
306 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:content"));
307 ///
308 /// assert_eq!(snaplog.scope(), &mut full::Snaplog::new("content"));
309 /// ```
310 pub fn scope_mut(&mut self) -> &mut full::Snaplog<T::Scope> {
311 &mut self.full
312 }
313
314 /// Returns a reference to the ignored part of this [`Snaplog`].
315 ///
316 /// # Examples
317 /// ```
318 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
319 /// let snaplog = Snaplog::new(Prefixed::new("prefix:content"));
320 ///
321 /// assert_eq!(snaplog.ignored(), &Some("prefix"));
322 /// ```
323 pub fn ignored(&self) -> &T::Ignored {
324 &self.ignored
325 }
326
327 /// Returns a mutable reference to the internal [`full::Snaplog`].
328 ///
329 /// # Examples
330 /// ```
331 /// # use snaplog::{full, scoped::{Snaplog, __Prefixed as Prefixed}};
332 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:content"));
333 ///
334 /// assert_eq!(snaplog.into_scope(), full::Snaplog::new("content"));
335 /// ```
336 pub fn into_scope(self) -> full::Snaplog<T::Scope> {
337 self.full
338 }
339}
340
341/// Implementations similar to [`full::Snaplog`].
342impl<T: IntoScoped> Snaplog<T> {
343 /// Records a snapshot in this [`Snaplog`].
344 ///
345 /// # Examples
346 /// ```
347 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
348 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
349 ///
350 /// snaplog.record("b");
351 /// snaplog.record("c");
352 /// assert_eq!(snaplog.history(), ["a", "b", "c"]);
353 /// ```
354 pub fn record(&mut self, snapshot: T::Scope) {
355 self.full.record(snapshot);
356 }
357
358 /// Records multiple snapshots in this [`Snaplog`].
359 ///
360 /// # Examples
361 /// ```
362 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
363 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
364 ///
365 /// snaplog.record_all(["b", "c", "d"]);
366 /// assert_eq!(snaplog.history(), ["a", "b", "c", "d"]);
367 /// ```
368 pub fn record_all<I>(&mut self, snapshots: I)
369 where
370 I: IntoIterator<Item = T::Scope>,
371 {
372 self.full.record_all(snapshots);
373 }
374
375 /// Records a change to the current element in this [`Snaplog`].
376 ///
377 /// # Examples
378 /// ```
379 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
380 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
381 ///
382 /// snaplog.record_change(|prev| { assert_eq!(prev, &"a"); "b" });
383 /// snaplog.record_change(|prev| { assert_eq!(prev, &"b"); "c" });
384 /// assert_eq!(snaplog.history(), ["a", "b", "c"]);
385 /// ```
386 pub fn record_change<F>(&mut self, f: F)
387 where
388 F: FnMut(&T::Scope) -> T::Scope,
389 {
390 self.full.record_change(f)
391 }
392
393 /// Records a change to the current element in this [`Snaplog`].
394 ///
395 /// # Errors
396 /// Returns the inner error if the closure failed.
397 ///
398 /// # Examples
399 /// ```
400 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
401 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
402 ///
403 /// snaplog.try_record_change(|prev| { assert_eq!(prev, &"a"); Ok("b") })?;
404 /// snaplog.try_record_change(|prev| { assert_eq!(prev, &"b"); Ok("c") })?;
405 /// assert_eq!(snaplog.try_record_change(|prev| Err(())), Err(()));
406 /// assert_eq!(snaplog.history(), ["a", "b", "c"]);
407 /// # Ok::<_, ()>(())
408 /// ```
409 pub fn try_record_change<F, E>(&mut self, f: F) -> Result<(), E>
410 where
411 F: FnMut(&T::Scope) -> Result<T::Scope, E>,
412 {
413 self.full.try_record_change(f)
414 }
415
416 /// Records multiple successive changes to the current element in this [`Snaplog`].
417 ///
418 /// # Examples
419 /// ```
420 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
421 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
422 ///
423 /// snaplog.record_changes_all(&mut ["b", "c", "d"], |change, _| *change);
424 /// assert_eq!(snaplog.history(), ["a", "b", "c", "d"]);
425 /// ```
426 pub fn record_changes_all<F, M>(&mut self, mutations: &mut [M], f: F)
427 where
428 F: FnMut(&mut M, &T::Scope) -> T::Scope,
429 {
430 self.full.record_changes_all(mutations, f);
431 }
432
433 /// Returns whether or not there are any changes recorded in this [`Snaplog`].
434 ///
435 /// # Examples
436 /// ```
437 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
438 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
439 ///
440 /// assert_eq!(snaplog.has_changes(), false);
441 /// snaplog.record("b");
442 /// snaplog.record("c");
443 /// assert_eq!(snaplog.has_changes(), true);
444 /// ```
445 pub fn has_changes(&self) -> bool {
446 self.full.has_changes()
447 }
448
449 /// Returns the initial element.
450 ///
451 /// # Examples
452 /// ```
453 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
454 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
455 ///
456 /// snaplog.record("b");
457 /// snaplog.record("c");
458 /// assert_eq!(snaplog.initial(), &"a");
459 /// ```
460 pub fn initial(&self) -> &T::Scope {
461 self.full.initial()
462 }
463
464 /// Returns the element at the given [`Select`].
465 ///
466 /// # Examples
467 /// ```
468 /// # use snaplog::{Select, scoped::{Snaplog, __Prefixed as Prefixed}};
469 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
470 ///
471 /// snaplog.record("b");
472 /// snaplog.record("c");
473 /// assert_eq!(snaplog.snapshot_at(Select::At(1)), &"b");
474 /// ```
475 pub fn snapshot_at(&self, select: Select) -> &T::Scope {
476 self.full.snapshot_at(select)
477 }
478
479 /// Returns the current element, that is the last recorded change or the initial element if
480 /// there are no none.
481 ///
482 /// # Examples
483 /// ```
484 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
485 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
486 ///
487 /// snaplog.record("b");
488 /// snaplog.record("c");
489 /// assert_eq!(snaplog.current(), &"c");
490 /// ```
491 pub fn current(&self) -> &T::Scope {
492 self.full.current()
493 }
494
495 /// Returns the initial element.
496 ///
497 /// # Examples
498 /// ```
499 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
500 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
501 ///
502 /// snaplog.record("b");
503 /// snaplog.record("c");
504 /// assert_eq!(snaplog.initial_mut(), &mut "a");
505 /// ```
506 pub fn initial_mut(&mut self) -> &mut T::Scope {
507 self.full.initial_mut()
508 }
509
510 /// Returns the element at the given [`Select`].
511 ///
512 /// # Examples
513 /// ```
514 /// # use snaplog::{Select, scoped::{Snaplog, __Prefixed as Prefixed}};
515 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
516 ///
517 /// snaplog.record("b");
518 /// snaplog.record("c");
519 /// assert_eq!(snaplog.snapshot_at_mut(Select::At(1)), &mut "b");
520 /// ```
521 pub fn snapshot_at_mut(&mut self, select: Select) -> &mut T::Scope {
522 self.full.snapshot_at_mut(select)
523 }
524
525 /// Returns the current element, that is the last recorded change or the initial element if
526 /// there are no none.
527 ///
528 /// # Examples
529 /// ```
530 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
531 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
532 ///
533 /// snaplog.record("b");
534 /// snaplog.record("c");
535 /// assert_eq!(snaplog.current_mut(), &mut "c");
536 /// ```
537 pub fn current_mut(&mut self) -> &mut T::Scope {
538 self.full.current_mut()
539 }
540
541 /// Clones the element at the given [`Select`].
542 ///
543 /// # Examples
544 /// ```
545 /// # use snaplog::{scoped::{Snaplog, __Prefixed as Prefixed}, Select};
546 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
547 ///
548 /// snaplog.record("b");
549 /// snaplog.record("c");
550 /// assert_eq!(snaplog.clone_snapshot_at(Select::At(1)), Prefixed::new("prefix:b"));
551 /// ```
552 pub fn clone_snapshot_at(&self, select: Select) -> T
553 where
554 T::Scope: Clone,
555 T::Ignored: Clone,
556 {
557 // TODO: let the user decide how to reconstruct from references? breaks IntoScoped interface
558 T::from_scoped(self.snapshot_at(select).clone(), self.ignored.clone())
559 }
560
561 /// Returns the full history recorded in this [`Snaplog`], including the initial element.
562 ///
563 /// # Examples
564 /// ```
565 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
566 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
567 ///
568 /// snaplog.record("b");
569 /// snaplog.record("c");
570 /// assert_eq!(snaplog.history(), ["a", "b", "c"]);
571 /// ```
572 pub fn history(&self) -> &[T::Scope] {
573 self.full.history()
574 }
575
576 /// Returns a mutable reference to the underlying `history`.
577 ///
578 /// # Examples
579 /// ```
580 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
581 /// let mut snaplog: Snaplog<Prefixed> = Snaplog::try_from_history(
582 /// ["a", "b", "c", "d", "e"],
583 /// None,
584 /// )?;
585 /// let history = snaplog.history_mut();
586 ///
587 /// history[0] = "g";
588 /// assert_eq!(snaplog.history(), ["g", "b", "c", "d", "e"]);
589 /// # Ok::<_, Box<dyn std::error::Error>>(())
590 /// ```
591 pub fn history_mut(&mut self) -> &mut [T::Scope] {
592 self.full.history_mut()
593 }
594
595 /// Drains the history in the specified range, a left open range is interpreted as starting
596 /// behind the initial element, elements that are not yielded from the [`Iterator`] are dropped.
597 ///
598 /// # Panics
599 /// Panics if the lower range bound is inclusive `0`.
600 /// Panics if the lower or upper bound are out of range.
601 ///
602 /// # Examples
603 /// ```
604 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
605 /// let mut snaplog: Snaplog<Prefixed> = Snaplog::try_from_history(
606 /// ["a", "b", "c", "d", "e"],
607 /// None,
608 /// )?;
609 ///
610 /// snaplog.drain(2..=3);
611 /// assert_eq!(snaplog.history(), ["a", "b", "e"]);
612 /// # Ok::<_, Box<dyn std::error::Error>>(())
613 /// ```
614 /// The unbounded range is reinterpreted as starting at `1`:
615 /// ```
616 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
617 /// # let mut snaplog: Snaplog<Prefixed> = Snaplog::try_from_history(["a", "b", "c", "d", "e"],
618 /// # None)?;
619 /// snaplog.drain(..);
620 /// assert_eq!(snaplog.history(), ["a"]);
621 /// # Ok::<_, Box<dyn std::error::Error>>(())
622 /// ```
623 /// The only invalid lower bound is `0`:
624 /// ```should_panic
625 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
626 /// # let mut snaplog: Snaplog<Prefixed> = Snaplog::try_from_history(["a", "b", "c", "d", "e"],
627 /// # None)?;
628 /// snaplog.drain(0..);
629 /// # Ok::<_, Box<dyn std::error::Error>>(())
630 /// ```
631 pub fn drain<'r, R>(&'r mut self, range: R) -> impl Iterator<Item = T::Scope> + 'r
632 where
633 R: RangeBounds<usize> + 'r,
634 {
635 self.full.drain(range)
636 }
637
638 /// Wipe the recorded history, keeping only the current element as the new initial element.
639 ///
640 /// # Examples
641 /// ```
642 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
643 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
644 ///
645 /// snaplog.record("b");
646 /// snaplog.record("c");
647 /// snaplog.clear_history();
648 /// assert_eq!(snaplog.initial(), &"c");
649 /// assert_eq!(snaplog.has_changes(), false);
650 /// ```
651 pub fn clear_history(&mut self) {
652 self.full.clear_history();
653 }
654
655 /// Wipe the recorded changes, keeping only the initial element.
656 ///
657 /// # Examples
658 /// ```
659 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
660 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
661 ///
662 /// snaplog.record("b");
663 /// snaplog.record("c");
664 /// snaplog.reset();
665 /// assert_eq!(snaplog.initial(), &"a");
666 /// assert_eq!(snaplog.has_changes(), false);
667 /// ```
668 pub fn reset(&mut self) {
669 self.full.reset();
670 }
671
672 /// Reserve space for `n` additional elements.
673 ///
674 /// # Examples
675 /// ```
676 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
677 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
678 /// snaplog.reserve(10);
679 /// ```
680 pub fn reserve(&mut self, n: usize) {
681 self.full.reserve(n);
682 }
683
684 /// Reserve space for `n` additional elements.
685 ///
686 /// # Errors
687 /// Returns an error if [`Vec::try_reserve`] failed.
688 ///
689 /// # Examples
690 /// ```
691 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
692 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
693 /// snaplog.try_reserve(10)?;
694 /// # Ok::<_, Box<dyn std::error::Error>>(())
695 /// ```
696 pub fn try_reserve(&mut self, n: usize) -> Result<(), TryReserveError> {
697 self.full.try_reserve(n)
698 }
699
700 /// Returns an iterator over references of the whole underling history.
701 ///
702 /// # Examples
703 /// ```
704 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
705 /// let mut snaplog: Snaplog<Prefixed> = Snaplog::try_from_history(
706 /// ["a", "b", "c", "d", "e"],
707 /// None,
708 /// )?;
709 ///
710 /// let mut copy = vec![];
711 /// for (snapshot, _) in snaplog.iter() {
712 /// copy.push(*snapshot);
713 /// }
714 ///
715 /// assert_eq!(copy, ["a", "b", "c", "d", "e"]);
716 /// # Ok::<_, Box<dyn std::error::Error>>(())
717 /// ```
718 pub fn iter(&self) -> Iter<'_, T> {
719 self.into_iter()
720 }
721
722 /// Returns an iterator over mutable references of the whole underling history.
723 ///
724 /// # Examples
725 /// ```
726 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
727 /// let mut snaplog: Snaplog<Prefixed> = Snaplog::try_from_history(
728 /// ["a", "b", "c", "d", "e"],
729 /// None,
730 /// )?;
731 ///
732 /// for (snapshot, _) in snaplog.iter_mut().filter(|&(&mut s, _)| s == "a" || s == "d") {
733 /// *snapshot = "f";
734 /// }
735 ///
736 /// assert_eq!(snaplog.history(), ["f", "b", "c", "f", "e"]);
737 /// # Ok::<_, Box<dyn std::error::Error>>(())
738 /// ```
739 pub fn iter_mut(&mut self) -> IterMut<'_, T> {
740 self.into_iter()
741 }
742
743 /// Unwrap the [`Snaplog`] into it's initial snapshot.
744 ///
745 /// # Examples
746 /// ```
747 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
748 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
749 ///
750 /// snaplog.record("b");
751 /// snaplog.record("c");
752 /// assert_eq!(snaplog.into_initial(), Prefixed::new("prefix:a"));
753 /// ```
754 pub fn into_initial(self) -> T {
755 T::from_scoped(self.full.into_initial(), self.ignored)
756 }
757
758 /// Unwrap the [`Snaplog`] into it's current snapshot.
759 ///
760 /// # Examples
761 /// ```
762 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
763 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
764 ///
765 /// snaplog.record("b");
766 /// snaplog.record("c");
767 /// assert_eq!(snaplog.into_current(), Prefixed::new("prefix:c"));
768 /// ```
769 pub fn into_current(self) -> T {
770 T::from_scoped(self.full.into_current(), self.ignored)
771 }
772
773 /// Unwrap the [`Snaplog`] into it's current snapshot.
774 ///
775 /// # Examples
776 /// ```
777 /// # use snaplog::{Select, scoped::{Snaplog, __Prefixed as Prefixed}};
778 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
779 ///
780 /// snaplog.record("b");
781 /// snaplog.record("c");
782 /// assert_eq!(snaplog.into_snapshot_at(Select::At(1)), Prefixed::new("prefix:b"));
783 /// ```
784 pub fn into_snapshot_at(self, select: Select) -> T {
785 T::from_scoped(self.full.into_snapshot_at(select), self.ignored)
786 }
787
788 /// Unwrap the [`Snaplog`] into it's history.
789 ///
790 /// # Examples
791 /// ```
792 /// # use snaplog::{full, scoped::{Snaplog, __Prefixed as Prefixed}};
793 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
794 ///
795 /// snaplog.record("b");
796 /// snaplog.record("c");
797 /// assert_eq!(
798 /// snaplog.into_inner(),
799 /// (full::Snaplog::from_history(["a", "b", "c"]), Some("prefix"))
800 /// );
801 /// ```
802 pub fn into_inner(self) -> (full::Snaplog<T::Scope>, T::Ignored) {
803 (self.full, self.ignored)
804 }
805}
806
807/// Thin implementations.
808impl<T: IntoScoped> Snaplog<T> {
809 /// Constructs a [`T::ThinScoped`][0] reference wrapper of the element at the given [`Select`].
810 ///
811 /// [0]: AsThinScoped::ThinScoped
812 ///
813 /// # Examples
814 /// ```
815 /// # use snaplog::{scoped::{Snaplog, __Prefixed as Prefixed}, Select};
816 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
817 ///
818 /// snaplog.record("b");
819 /// snaplog.record("c");
820 /// // note that the equality impl of T::ThinScoped and &T is given for this example
821 /// assert_eq!(snaplog.thin_snapshot_at(Select::At(1)), &Prefixed::new("prefix:b"));
822 /// ```
823 pub fn thin_snapshot_at(&self, select: Select) -> T::ThinScoped<'_>
824 where
825 T: AsThinScoped,
826 {
827 T::as_thin_scoped(self.snapshot_at(select), &self.ignored)
828 }
829
830 /// Constructs a [`T::ThinScopedMut`][0] reference wrapper of the element at the given
831 /// [`Select`].
832 ///
833 /// [0]: AsThinScopedMut::ThinScopedMut
834 ///
835 /// # Examples
836 /// ```
837 /// # use snaplog::{scoped::{Snaplog, __Prefixed as Prefixed}, Select};
838 /// let mut snaplog = Snaplog::new(Prefixed::new("prefix:a"));
839 ///
840 /// snaplog.record("b");
841 /// snaplog.record("c");
842 /// // note that the equality impl of T::ThinScopedMut and &T is given for this example
843 /// assert_eq!(snaplog.thin_snapshot_at_mut(Select::At(1)), &mut Prefixed::new("prefix:b"));
844 /// ```
845 pub fn thin_snapshot_at_mut(&mut self, select: Select) -> T::ThinScopedMut<'_>
846 where
847 T: AsThinScopedMut,
848 {
849 T::as_thin_scoped_mut(self.full.snapshot_at_mut(select), &mut self.ignored)
850 }
851}
852
853/// Unsafe implementations.
854impl<T: IntoScoped> Snaplog<T> {
855 /// Creates a new [`Snaplog`] for the given `history` backing vector.
856 ///
857 /// # Safety
858 /// The caller must ensure that the [`Vec`] contains at least one element.
859 ///
860 /// # Examples
861 /// ```
862 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
863 /// // this is fine
864 /// let snaplog: Snaplog<Prefixed> = unsafe {
865 /// Snaplog::from_vec_unchecked(vec!["content"], None)
866 /// };
867 ///
868 /// // this will later fail
869 /// let snaplog: Snaplog<Prefixed> = unsafe {
870 /// Snaplog::from_vec_unchecked(vec![], Some("prefix"))
871 /// };
872 /// ```
873 pub unsafe fn from_vec_unchecked(history: Vec<T::Scope>, ignored: T::Ignored) -> Self {
874 Self {
875 // SAFETY: invariants must be upheld by the caller
876 full: unsafe { full::Snaplog::from_vec_unchecked(history) },
877 ignored,
878 }
879 }
880
881 /// Creates a new [`Snaplog`] for the given `history` backing vector.
882 ///
883 /// # Safety
884 /// The caller must ensure that the `iter` contains at least one element.
885 ///
886 /// # Examples
887 /// ```
888 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
889 /// // this is fine
890 /// let snaplog: Snaplog<Prefixed> = unsafe {
891 /// Snaplog::from_history_unchecked(["a", "b", "c"], None)
892 /// };
893 ///
894 /// // this will later fail
895 /// let snaplog: Snaplog<Prefixed> = unsafe {
896 /// Snaplog::from_history_unchecked(std::iter::empty(), Some("prefix"))
897 /// };
898 /// ```
899 pub unsafe fn from_history_unchecked<I>(history: I, ignored: T::Ignored) -> Self
900 where
901 I: IntoIterator<Item = T::Scope>,
902 {
903 // SAFETY: invariants must be upheld by the caller
904 unsafe { Self::from_vec_unchecked(history.into_iter().collect(), ignored) }
905 }
906
907 /// Returns a mutable reference to the underlying [`Vec`]. The first element of this vector is
908 /// the initial element and is always set.
909 ///
910 /// # Safety
911 /// The caller must ensure that the [`Vec`] retains at least one element after mutation, this
912 /// element serves as the initial element.
913 ///
914 /// # Examples
915 /// ```
916 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
917 /// let mut snaplog: Snaplog<Prefixed> = Snaplog::try_from_history(
918 /// ["a", "b", "c", "d", "e", "f", "g"],
919 /// None,
920 /// )?;
921 ///
922 /// // SAFETY: no elements are removed
923 /// let inner = unsafe { snaplog.history_mut_vec() };
924 /// inner[5] = "h";
925 /// inner[6] = "i";
926 /// inner.drain(1..=3);
927 /// inner.push("j");
928 ///
929 /// assert_eq!(snaplog.history(), ["a", "e", "h", "i", "j"]);
930 /// # Ok::<_, Box<dyn std::error::Error>>(())
931 /// ```
932 pub unsafe fn history_mut_vec(&mut self) -> &mut Vec<T::Scope> {
933 // SAFETY: invariants must be upheld by the caller
934 unsafe { self.full.history_mut_vec() }
935 }
936
937 /// Returns a mutable reference to the ignored part.
938 ///
939 /// # Safety
940 /// There are no safety concerns in the current version but this is unsafe because mutation that
941 /// changes things like Hashing may not be expected in other parts of the code, the caller must
942 /// uphold invariants over the ignored part's mutation.
943 ///
944 /// # Examples
945 /// ```
946 /// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed};
947 /// let mut snaplog: Snaplog<Prefixed> = Snaplog::try_from_history(
948 /// ["a", "b", "c", "d", "e"],
949 /// None,
950 /// )?;
951 ///
952 /// // SAFETY: there are no invariants regarding Prefixed not having it's prefix mutated
953 /// let inner = unsafe { snaplog.ignored_mut() };
954 /// *inner = Some("new_prefix");
955 ///
956 /// assert_eq!(snaplog.into_current(), Prefixed::new("new_prefix:e"));
957 /// # Ok::<_, Box<dyn std::error::Error>>(())
958 /// ```
959 pub unsafe fn ignored_mut(&mut self) -> &mut T::Ignored {
960 &mut self.ignored
961 }
962}
963
964// first class traits
965impl<T: IntoScoped> Clone for Snaplog<T>
966where
967 T::Scope: Clone,
968 T::Ignored: Clone,
969{
970 fn clone(&self) -> Self {
971 Self {
972 full: self.full.clone(),
973 ignored: self.ignored.clone(),
974 }
975 }
976
977 fn clone_from(&mut self, source: &Self) {
978 self.full.clone_from(&source.full);
979 self.ignored.clone_from(&source.ignored);
980 }
981}
982
983impl<T: IntoScoped> std::ops::Index<Select> for Snaplog<T> {
984 type Output = T::Scope;
985
986 fn index(&self, index: Select) -> &Self::Output {
987 self.snapshot_at(index)
988 }
989}
990
991impl<T: IntoScoped> std::ops::IndexMut<Select> for Snaplog<T> {
992 fn index_mut(&mut self, index: Select) -> &mut Self::Output {
993 self.snapshot_at_mut(index)
994 }
995}
996
997// iter
998impl<T: IntoScoped> std::iter::Extend<T::Scope> for Snaplog<T> {
999 fn extend<I: IntoIterator<Item = T::Scope>>(&mut self, iter: I) {
1000 self.full.extend(iter);
1001 }
1002}
1003
1004// iter
1005/// An [`Iterator`] over all snapshot scopes and references to the ignored part.
1006///
1007/// # Examples
1008/// ```
1009/// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed, IntoScoped};
1010/// # type Scope = &'static str;
1011/// # type Ignored = Option<&'static str>;
1012/// let mut snaplog = Snaplog::new(Prefixed::new("prefix:content"));
1013/// let mut iter = snaplog.into_iter();
1014///
1015/// for snapshot in iter {
1016/// let s: (Scope, Ignored) = snapshot;
1017/// }
1018///
1019/// let mut snaplog = Snaplog::new(Prefixed::new("prefix:content"));
1020///
1021/// for snapshot in snaplog {
1022/// let s: (Scope, Ignored) = snapshot;
1023/// }
1024/// ```
1025#[derive(Debug)]
1026pub struct IntoIter<T: IntoScoped> {
1027 inner: full::IntoIter<T::Scope>,
1028 ignored: T::Ignored,
1029}
1030
1031impl<T: IntoScoped> IntoIter<T> {
1032 /// Returns a reference to the ignored part.
1033 pub fn ignored(&self) -> &T::Ignored {
1034 &self.ignored
1035 }
1036}
1037
1038impl<T: IntoScoped> Iterator for IntoIter<T>
1039where
1040 T::Ignored: Clone,
1041{
1042 type Item = (T::Scope, T::Ignored);
1043
1044 fn next(&mut self) -> Option<Self::Item> {
1045 // TODO: reduce last unnecessary clone by using a peeking iter and storing it as an Option
1046 self.inner.next().map(|s| (s, self.ignored.clone()))
1047 }
1048}
1049
1050impl<T: IntoScoped> IntoIterator for Snaplog<T>
1051where
1052 T::Ignored: Clone,
1053{
1054 type Item = (T::Scope, T::Ignored);
1055 type IntoIter = IntoIter<T>;
1056
1057 fn into_iter(self) -> Self::IntoIter {
1058 Self::IntoIter {
1059 inner: self.full.into_iter(),
1060 ignored: self.ignored,
1061 }
1062 }
1063}
1064
1065// TODO: exact size iter etc
1066
1067/// An [`Iterator`] over references to snapshot scopes and references to the ignored part.
1068///
1069/// # Examples
1070/// ```
1071/// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed, IntoScoped};
1072/// # type Scope = &'static str;
1073/// # type Ignored = Option<&'static str>;
1074/// let mut snaplog = Snaplog::new(Prefixed::new("prefix:content"));
1075///
1076/// for snapshot in snaplog.iter() {
1077/// let s: (&Scope, &Ignored) = snapshot;
1078/// }
1079///
1080/// for snapshot in &snaplog {
1081/// let s: (&Scope, &Ignored) = snapshot;
1082/// }
1083/// ```
1084#[derive(Debug)]
1085pub struct Iter<'cl, T: IntoScoped> {
1086 inner: full::Iter<'cl, T::Scope>,
1087 ignored: &'cl T::Ignored,
1088}
1089
1090impl<'cl, T: IntoScoped> Iter<'cl, T> {
1091 /// Returns a reference to the ignored part.
1092 pub fn ignored(&self) -> &'cl T::Ignored {
1093 self.ignored
1094 }
1095}
1096
1097impl<'cl, T: IntoScoped> Iterator for Iter<'cl, T> {
1098 type Item = (&'cl T::Scope, &'cl T::Ignored);
1099
1100 fn next(&mut self) -> Option<Self::Item> {
1101 self.inner.next().map(|s| (s, self.ignored))
1102 }
1103}
1104
1105impl<'cl, T: IntoScoped> IntoIterator for &'cl Snaplog<T> {
1106 type Item = (&'cl T::Scope, &'cl T::Ignored);
1107 type IntoIter = Iter<'cl, T>;
1108
1109 fn into_iter(self) -> Self::IntoIter {
1110 Iter {
1111 inner: self.full.iter(),
1112 ignored: &self.ignored,
1113 }
1114 }
1115}
1116
1117/// An [`Iterator`] over mutable references to snapshot scopes and references to the ignored part.
1118///
1119/// # Examples
1120/// ```
1121/// # use snaplog::scoped::{Snaplog, __Prefixed as Prefixed, IntoScoped};
1122/// # type Scope = &'static str;
1123/// # type Ignored = Option<&'static str>;
1124/// let mut snaplog = Snaplog::new(Prefixed::new("prefix:content"));
1125///
1126/// for snapshot in snaplog.iter_mut() {
1127/// let s: (&mut Scope, &Ignored) = snapshot;
1128/// }
1129///
1130/// for snapshot in &mut snaplog {
1131/// let s: (&mut Scope, &Ignored) = snapshot;
1132/// }
1133/// ```
1134#[derive(Debug)]
1135pub struct IterMut<'cl, T: IntoScoped> {
1136 inner: full::IterMut<'cl, T::Scope>,
1137 ignored: &'cl T::Ignored,
1138}
1139
1140impl<'cl, T: IntoScoped> IterMut<'cl, T> {
1141 /// Returns a reference to the ignored part.
1142 pub fn ignored(&self) -> &'cl T::Ignored {
1143 self.ignored
1144 }
1145}
1146
1147impl<'cl, T: IntoScoped> Iterator for IterMut<'cl, T> {
1148 type Item = (&'cl mut T::Scope, &'cl T::Ignored);
1149
1150 fn next(&mut self) -> Option<Self::Item> {
1151 self.inner.next().map(|s| (s, self.ignored))
1152 }
1153}
1154
1155impl<'cl, T: IntoScoped> IntoIterator for &'cl mut Snaplog<T> {
1156 type Item = (&'cl mut T::Scope, &'cl T::Ignored);
1157 type IntoIter = IterMut<'cl, T>;
1158
1159 fn into_iter(self) -> Self::IntoIter {
1160 IterMut {
1161 inner: self.full.iter_mut(),
1162 ignored: &mut self.ignored,
1163 }
1164 }
1165}
1166
1167// conversions
1168impl<T: IntoScoped> From<T> for Snaplog<T> {
1169 fn from(initial: T) -> Self {
1170 Self::new(initial)
1171 }
1172}
1173
1174impl<T: IntoScoped> From<Snaplog<T>> for (full::Snaplog<T::Scope>, T::Ignored) {
1175 fn from(snaplog: Snaplog<T>) -> Self {
1176 snaplog.into_inner()
1177 }
1178}
1179
1180impl<T: IntoScoped> TryFrom<(Vec<T::Scope>, T::Ignored)> for Snaplog<T> {
1181 type Error = EmptyHistoryError;
1182
1183 fn try_from(value: (Vec<T::Scope>, T::Ignored)) -> Result<Self, Self::Error> {
1184 Self::try_from_vec(value.0, value.1)
1185 }
1186}