~iptq/garbage

ref: 45fc4f1ee1bf4703e50eaed3e5f61ee06a7f6f81 garbage/src/info.rs -rw-r--r-- 3.2 KiB
45fc4f1e — Michael Zhang add sourcehut build badge and remove deps.rs one 1 year, 3 months ago
                                                                                
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
use std::fs::File;
use std::io::{self, BufRead, BufReader, Write};
use std::path::{Path, PathBuf};

use chrono::{DateTime, Local, TimeZone};

use crate::errors::{Error, TrashInfoError};

const DATE_FORMAT: &str = "%Y-%m-%dT%H:%M:%S";

fn parse_key_value(line: &str) -> Option<(&str, &str)> {
    let mut parts = line.split('=').peekable();
    let key = if let Some(key) = parts.next() {
        key
    } else {
        return None;
    };

    let value = &line[key.len() + 1..];
    Some((key, value))
}

/// .trashinfo Data
#[derive(Debug)]
pub struct TrashInfo {
    /// The original path where this file was located before it was deleted.
    pub path: PathBuf,

    /// The date the file was deleted.
    pub deletion_date: DateTime<Local>,

    /// The location of the deleted file after deletion.
    pub deleted_path: PathBuf,

    /// The location of the `info` description file.
    pub info_path: PathBuf,
}

impl TrashInfo {
    /// Create a new TrashInfo based on the .trashinfo path and the deleted file path
    ///
    /// This is useful for reading files from the Trash.
    pub fn from_files(
        info_path: impl AsRef<Path>,
        deleted_path: impl AsRef<Path>,
    ) -> Result<Self, Error> {
        let info_path = info_path.as_ref().to_path_buf();
        let deleted_path = deleted_path.as_ref().to_path_buf();
        let file = File::open(&info_path)?;
        let reader = BufReader::new(file);

        let mut path = None;
        let mut deletion_date = None;

        for (i, line) in reader.lines().enumerate() {
            let line = line?;

            // first line must be "[Trash Info]"
            if i == 0 {
                if line != "[Trash Info]" {
                    return Err(Error::BadTrashInfo(TrashInfoError::MissingHeader));
                } else {
                    continue;
                }
            }

            if let Some((key, value)) = parse_key_value(&line) {
                match key {
                    "Path" => {
                        let value = PathBuf::from(value);
                        path = Some(value)
                    }
                    "DeletionDate" => {
                        let date = Local.datetime_from_str(value, DATE_FORMAT)?;
                        deletion_date = Some(date)
                    }
                    _ => continue,
                }
            } else {
                continue;
            }
        }

        let path = match path {
            Some(path) => path,
            None => return Err(Error::BadTrashInfo(TrashInfoError::MissingPath)),
        };

        let deletion_date = match deletion_date {
            Some(deletion_date) => deletion_date,
            None => return Err(Error::BadTrashInfo(TrashInfoError::MissingDate)),
        };

        Ok(TrashInfo {
            path,
            deletion_date,
            deleted_path,
            info_path,
        })
    }

    /// Write the current TrashInfo into a .trashinfo file.
    pub fn write(&self, mut out: impl Write) -> Result<(), io::Error> {
        writeln!(out, "[Trash Info]")?;
        writeln!(out, "Path={}", self.path.to_str().unwrap())?;
        writeln!(
            out,
            "DeletionDate={}",
            self.deletion_date.format(DATE_FORMAT)
        )?;
        Ok(())
    }
}