1mod error;
5pub mod messages;
6
7use std::time::{Duration, SystemTime};
8
9use server::{AsScalar, CheckedConn, CheckedPermissions, FromScalar, MessageAllowed, Server, ServerContext};
10
11pub use crate::error::Error;
12use crate::messages::*;
13
14pub const DO_NOT_BACKUP_FOLDER: &str = "no_backup";
16
17#[macro_export]
18macro_rules! use_api {
19 () => {
20 mod backup_permissions {
21 use backup::messages::*;
22 #[derive(Clone, Default, server::Permissions)]
23 #[server_name = "os/backup"]
24 pub struct BackupPermissions;
25 }
26 type BackupApi = backup::BackupApi<backup_permissions::BackupPermissions>;
27 };
28}
29
30#[derive(Default)]
31pub struct BackupApi<P: CheckedPermissions> {
32 conn: CheckedConn<P>,
33}
34
35impl<P: CheckedPermissions> BackupApi<P> {
36 pub fn create_backup(&self) -> Result<(), Error>
37 where
38 P: MessageAllowed<CreateBackup>,
39 {
40 self.conn.send_blocking_archive(CreateBackup)
41 }
42
43 pub fn create_backup_file(&self, backup_path: String, location: fs::Location) -> Result<(), Error>
44 where
45 P: MessageAllowed<CreateBackupFile>,
46 {
47 self.conn.send_blocking_archive(CreateBackupFile { backup_path, location })
48 }
49
50 pub fn restore_backup(&self, backup_path: String, location: fs::Location) -> Result<(), Error>
51 where
52 P: MessageAllowed<RestoreBackup>,
53 {
54 self.conn.send_blocking_archive(RestoreBackup { backup_path, location })
55 }
56
57 pub fn subscribe_restore_progress<S>(&self, context: &mut ServerContext<S>)
58 where
59 P: MessageAllowed<SubscribeRestoreProgress>,
60 S: Server + server::ArchiveEventHandler<RestoreProgress>,
61 {
62 self.conn.subscribe_archive_infallible(SubscribeRestoreProgress, context);
63 }
64}
65
66#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
67pub struct Status {
68 pub last_backup_at: Option<SystemTime>,
69 pub publish_failed: bool,
70}
71
72impl FromScalar<3> for Status {
73 fn from_scalar([last_backed_up_at_h, last_backed_up_at_l, publish_failed]: [u32; 3]) -> Self {
74 let last_backup_at = if last_backed_up_at_h != 0 || last_backed_up_at_l != 0 {
75 let h = last_backed_up_at_h.to_le_bytes();
76 let l = last_backed_up_at_l.to_le_bytes();
77 let last_backed_up_at_u64 = u64::from_le_bytes([h[0], h[1], h[2], h[3], l[0], l[1], l[2], l[3]]);
78 Some(SystemTime::UNIX_EPOCH + Duration::from_secs(last_backed_up_at_u64))
79 } else {
80 None
81 };
82
83 Status { last_backup_at, publish_failed: publish_failed != 0 }
84 }
85}
86
87impl AsScalar<3> for Status {
88 fn as_scalar(&self) -> [u32; 3] {
89 let (last_backed_up_at_h, last_backed_up_at_l) = if let Some(last_backup_at) = self.last_backup_at {
90 let [h1, h2, h3, h4, l1, l2, l3, l4] =
91 last_backup_at.duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs().to_le_bytes();
92 (u32::from_le_bytes([h1, h2, h3, h4]), u32::from_le_bytes([l1, l2, l3, l4]))
93 } else {
94 (0, 0)
95 };
96
97 [last_backed_up_at_h, last_backed_up_at_l, u32::from(self.publish_failed)]
98 }
99}