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}