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
// Conserve backup system.
// Copyright 2015-2023 Martin Pool.

// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

//! An entry representing a file, directory, etc, in either a
//! stored tree or local tree.

use std::borrow::Borrow;
use std::fmt::Debug;

use serde::Serialize;
use time::OffsetDateTime;

use crate::kind::Kind;
use crate::owner::Owner;
use crate::unix_mode::UnixMode;
use crate::*;

/// A description of an file, directory, or symlink in a tree, independent
/// of whether it's recorded in a archive (an [IndexEntry]), or
/// in a source tree.
// TODO: Maybe keep this entirely in memory and explicitly look things
// up when needed.
pub trait EntryTrait: Debug {
    fn apath(&self) -> &Apath;
    fn kind(&self) -> Kind;
    fn mtime(&self) -> OffsetDateTime;
    fn size(&self) -> Option<u64>;
    fn symlink_target(&self) -> Option<&str>;
    fn unix_mode(&self) -> UnixMode;
    fn owner(&self) -> &Owner;
}

/// Per-kind metadata.
#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
#[serde(tag = "kind")]
pub enum KindMeta {
    File { size: u64 },
    Dir,
    Symlink { target: String },
    Unknown,
}

impl From<&KindMeta> for Kind {
    fn from(from: &KindMeta) -> Kind {
        match from {
            KindMeta::Dir => Kind::Dir,
            KindMeta::File { .. } => Kind::File,
            KindMeta::Symlink { .. } => Kind::Symlink,
            KindMeta::Unknown => Kind::Unknown,
        }
    }
}

/// An in-memory [Entry] describing a file/dir/symlink, with no addresses.
#[derive(Debug, Serialize, Clone, Eq, PartialEq)]
pub struct EntryValue {
    pub(crate) apath: Apath,

    /// Is it a file, dir, or symlink, and for files the size and for symlinks the target.
    #[serde(flatten)]
    pub(crate) kind_meta: KindMeta,

    /// Modification time.
    pub(crate) mtime: OffsetDateTime,
    pub(crate) unix_mode: UnixMode,
    #[serde(flatten)]
    pub(crate) owner: Owner,
}

impl<B: Borrow<EntryValue> + Debug> EntryTrait for B {
    fn apath(&self) -> &Apath {
        &self.borrow().apath
    }

    fn kind(&self) -> Kind {
        Kind::from(&self.borrow().kind_meta)
    }

    fn mtime(&self) -> OffsetDateTime {
        self.borrow().mtime
    }

    fn size(&self) -> Option<u64> {
        if let KindMeta::File { size } = self.borrow().kind_meta {
            Some(size)
        } else {
            None
        }
    }

    fn symlink_target(&self) -> Option<&str> {
        match &self.borrow().kind_meta {
            KindMeta::Symlink { target } => Some(target),
            _ => None,
        }
    }

    fn unix_mode(&self) -> UnixMode {
        self.borrow().unix_mode
    }

    fn owner(&self) -> &Owner {
        &self.borrow().owner
    }
}