1use crate::CloudProvider;
5use crate::{BootstrapError, StorageConfig, StoreId};
6use coven_crypto::custody::{Keychain, PersonEntries, PersonRestoreCode};
7use coven_sync::UnreadableSavedCode;
8use std::sync::Arc;
9
10#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct SavedRestoreCode {
14 pub name: String,
18 pub store_id: StoreId,
20 pub location: StorageConfig,
23 pub has_s3_credentials: bool,
25}
26
27#[derive(Clone, Debug, PartialEq, Eq)]
29pub enum SavedRestoreEntry {
30 Readable(SavedRestoreCode),
32 Unreadable {
34 store_id: StoreId,
36 reason: UnreadableSavedCode,
38 },
39}
40
41impl SavedRestoreEntry {
42 pub fn store_id(&self) -> StoreId {
44 match self {
45 Self::Readable(saved) => saved.store_id,
46 Self::Unreadable { store_id, .. } => *store_id,
47 }
48 }
49}
50
51pub(crate) fn list(keychain: Arc<Keychain>) -> Result<Vec<SavedRestoreEntry>, BootstrapError> {
52 keychain
53 .person_restore_codes()?
54 .into_iter()
55 .map(|(store, code)| entry(&PersonEntries::new(keychain.clone(), store), code))
56 .collect()
57}
58
59pub(crate) fn entry(
62 person: &PersonEntries,
63 code: PersonRestoreCode,
64) -> Result<SavedRestoreEntry, BootstrapError> {
65 let store_id = person.store();
66 let decoded = match code {
67 PersonRestoreCode::Saved(bytes) => coven_sync::read_saved_code(store_id, bytes.as_bytes()),
68 PersonRestoreCode::Ambiguous => Err(UnreadableSavedCode::Ambiguous),
69 };
70 let (saved, location) = match decoded {
71 Ok(decoded) => decoded,
72 Err(reason) => return Ok(SavedRestoreEntry::Unreadable { store_id, reason }),
73 };
74 let has_s3_credentials =
75 location.provider() == CloudProvider::S3 && person.s3_credentials()?.is_some();
76 Ok(SavedRestoreEntry::Readable(SavedRestoreCode {
77 name: saved.name,
78 store_id,
79 location,
80 has_s3_credentials,
81 }))
82}
83
84pub(crate) fn forget(keychain: Arc<Keychain>, store: StoreId) -> Result<(), BootstrapError> {
85 Ok(PersonEntries::new(keychain, store).forget()?)
86}
87
88#[cfg(test)]
89#[path = "saved_restore_tests.rs"]
90mod tests;