summaryrefslogtreecommitdiff
path: root/src/backup.rs
blob: 69bc2ea88efc1369a2d3334f7208b884b100a6a5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use std::{
    fs::{create_dir_all, File, OpenOptions},
    io::{ErrorKind, Read, Write},
    path::PathBuf,
    time::{SystemTime, UNIX_EPOCH},
};

use gethostname::gethostname;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use crate::{
    config::Config,
    error::{Error, Result},
    packages::Package,
    pathinfo::PathInfo,
};

pub type BackupId = String;

#[derive(Debug, Serialize, Deserialize)]
pub struct Backup {
    id: String,
    timestamp: u64,
    packages: Vec<Package>,
    files: Vec<PathInfo>,
}

impl Backup {
    pub fn create(config: &Config, packages: Vec<Package>) -> Result<Self> {
        let mut files: Vec<PathInfo> = Vec::new();
        for dir in &config.directories {
            files.push(PathInfo::from_path(config, dir)?);
        }
        Ok(Self {
            // UUID not really needed, maybe a shorter hash
            id: Uuid::new_v4().to_string(),
            timestamp: Self::get_timestamp(),
            packages,
            files,
        })
    }

    pub fn save(&self, config: &Config) -> Result<()> {
        let rel_location = format!(
            "bu_{}_{}",
            gethostname()
                .into_string()
                .map_err(|_| Error::InvalidOsString)?,
            Self::get_timestamp()
        );

        let bl = BackupLocation {
            id: self.id.to_string(),
            rel_location,
        };

        Self::append_to_root_index(config, bl.clone())?;

        let backup_root = format!("{}/{}", config.root, bl.rel_location);
        create_dir_all(&backup_root).unwrap();
        let path = format!("{}/index.json", backup_root);
        let mut f = File::create(path).unwrap();
        f.write_all(&serde_json::to_vec(self).unwrap()).unwrap();

        Ok(())
    }

    pub fn get(config: &Config, _id: Option<BackupId>) -> Result<()> {
        let backup_index_root = format!("{}/index.json", config.root);
        let mut file = File::open(backup_index_root)?;
        let mut content = String::new();
        file.read_to_string(&mut content)?;
        let list: Vec<BackupLocation> = serde_json::from_str(&content)?;
        println!("{list:#?}");

        todo!();

        Ok(())
    }

    fn append_to_root_index(config: &Config, new_backup: BackupLocation) -> Result<()> {
        let backup_index_root = format!("{}/index.json", config.root);
        let path = PathBuf::from(&backup_index_root);
        if path.exists() {
            let mut f = File::open(&path)?;
            let mut content = String::new();
            f.read_to_string(&mut content)?;
            let mut loc: Vec<BackupLocation> = serde_json::from_str(&content)?;

            let mut f = File::create(path)?;
            loc.push(new_backup);

            f.write_all(&serde_json::to_vec(&loc)?)?;
        } else {
            let mut f = File::create(backup_index_root)?;
            f.write_all(&serde_json::to_vec(&vec![new_backup])?)?;
        };

        Ok(())
    }

    fn get_timestamp() -> u64 {
        SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs()
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct BackupLocation {
    id: BackupId,
    rel_location: String,
}