nuts_container/migrate.rs
1// MIT License
2//
3// Copyright (c) 2024 Robin Doer
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to
7// deal in the Software without restriction, including without limitation the
8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9// sell copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in
13// all copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21// IN THE SOFTWARE.
22
23#[cfg(test)]
24mod tests;
25
26use std::fmt;
27use thiserror::Error;
28
29use crate::svec::SecureVec;
30
31#[derive(Debug, Error)]
32pub enum MigrationError {
33 /// Failed to migrate from rev0 to rev1
34 #[error("failed to migrate the revision 0 header into revision 1")]
35 Rev0(String),
36}
37
38pub trait Migration {
39 /// Migration of a revision 0 header.
40 ///
41 /// You you have a revision 0 header, then this function is called. You
42 /// must extract the `top-id` from the given `userdata` record. The concept
43 /// of `userdata` was removed with the revision 1. The `top-id` is directly
44 /// stored in the header.
45 ///
46 /// On success the extracted `top-id` should be returned, otherwise return
47 /// an error-description.
48 fn migrate_rev0(&self, userdata: &[u8]) -> Result<(u32, Vec<u8>), String>;
49}
50
51#[derive(Default)]
52pub struct Migrator<'a>(Option<Box<dyn Migration + 'a>>);
53
54impl<'a> Migrator<'a> {
55 pub fn with_migration<M: 'a + Migration>(mut self, migration: M) -> Self {
56 self.0 = Some(Box::new(migration));
57 self
58 }
59
60 pub fn migrate_rev0(
61 &self,
62 userdata: &[u8],
63 ) -> Result<Option<(u32, SecureVec)>, MigrationError> {
64 if let Some(migration) = self.0.as_ref() {
65 match migration.migrate_rev0(userdata) {
66 Ok((sid, top_id)) => Ok(Some((sid, top_id.into()))),
67 Err(err) => Err(MigrationError::Rev0(err)),
68 }
69 } else {
70 Ok(None)
71 }
72 }
73}
74
75impl<'a> fmt::Debug for Migrator<'a> {
76 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
77 fmt.debug_tuple("Migrator")
78 .field(&self.0.as_ref().map(|_| "..."))
79 .finish()
80 }
81}