liboxen/model/entry/
commit_entry.rs

1use crate::constants::VERSION_FILE_NAME;
2use crate::model::merkle_tree::node::{DirNode, FileNode};
3use crate::model::{Commit, ContentHashable, LocalRepository, RemoteEntry, Schema};
4use crate::util;
5
6use filetime::FileTime;
7use serde::{Deserialize, Serialize};
8use std::env;
9use std::hash::{Hash, Hasher};
10use std::path::{Path, PathBuf};
11
12#[derive(Clone, Debug)]
13pub enum Entry {
14    CommitEntry(CommitEntry),
15    SchemaEntry(SchemaEntry),
16}
17
18impl Hash for Entry {
19    fn hash<H: Hasher>(&self, state: &mut H) {
20        match self {
21            Entry::CommitEntry(entry) => entry.hash.hash(state),
22            Entry::SchemaEntry(entry) => entry.hash.hash(state),
23        }
24    }
25}
26
27impl PartialEq for Entry {
28    fn eq(&self, other: &Entry) -> bool {
29        self.hash() == other.hash()
30    }
31}
32
33impl Eq for Entry {}
34
35impl Entry {
36    pub fn commit_id(&self) -> String {
37        match self {
38            Entry::CommitEntry(entry) => entry.commit_id.clone(),
39            Entry::SchemaEntry(entry) => entry.commit_id.clone(),
40        }
41    }
42
43    pub fn path(&self) -> PathBuf {
44        match self {
45            Entry::CommitEntry(entry) => entry.path.clone(),
46            Entry::SchemaEntry(entry) => entry.path.clone(),
47        }
48    }
49
50    pub fn hash(&self) -> String {
51        match self {
52            Entry::CommitEntry(entry) => entry.hash.clone(),
53            Entry::SchemaEntry(entry) => entry.hash.clone(),
54        }
55    }
56
57    pub fn num_bytes(&self) -> u64 {
58        match self {
59            Entry::CommitEntry(entry) => entry.num_bytes,
60            Entry::SchemaEntry(entry) => entry.num_bytes,
61        }
62    }
63    pub fn extension(&self) -> String {
64        match self {
65            Entry::CommitEntry(entry) => entry.extension(),
66            Entry::SchemaEntry(_entry) => "".to_string(),
67        }
68    }
69}
70
71// get a From for entry
72impl From<CommitEntry> for Entry {
73    fn from(entry: CommitEntry) -> Self {
74        Entry::CommitEntry(entry)
75    }
76}
77
78impl From<SchemaEntry> for Entry {
79    fn from(entry: SchemaEntry) -> Self {
80        Entry::SchemaEntry(entry)
81    }
82}
83
84impl From<Entry> for CommitEntry {
85    fn from(entry: Entry) -> Self {
86        match entry {
87            Entry::CommitEntry(entry) => entry,
88            _ => panic!("Cannot convert Entry to CommitEntry"),
89        }
90    }
91}
92
93#[derive(Deserialize, Serialize, Debug, Clone)]
94pub struct CommitPath {
95    pub commit: Option<Commit>,
96    pub path: PathBuf,
97}
98
99#[derive(Deserialize, Serialize, Debug, Clone)]
100pub struct CommitEntry {
101    pub commit_id: String,
102    pub path: PathBuf,
103    pub hash: String,
104    pub num_bytes: u64,
105    pub last_modified_seconds: i64,
106    pub last_modified_nanoseconds: u32,
107}
108
109// TODONOW - maybe rename or reorg, this isn't an "entry" as such
110#[derive(Deserialize, Serialize, Debug, Clone)]
111pub struct CompareEntry {
112    pub commit_entry: Option<CommitEntry>,
113    pub path: PathBuf,
114}
115
116#[derive(Deserialize, Serialize, Debug, Clone)]
117pub struct SchemaEntry {
118    pub commit_id: String,
119    pub path: PathBuf,
120    pub hash: String,
121    pub num_bytes: u64,
122}
123
124impl SchemaEntry {
125    pub fn new(commit_id: String, path: PathBuf, schema: Schema) -> SchemaEntry {
126        SchemaEntry {
127            commit_id,
128            path,
129            hash: schema.hash.clone(),
130            num_bytes: schema.num_bytes(),
131        }
132    }
133}
134
135impl ContentHashable for CommitEntry {
136    fn content_hash(&self) -> String {
137        self.hash.clone()
138    }
139}
140
141// Hash on the path field so we can quickly look up
142impl PartialEq for CommitEntry {
143    fn eq(&self, other: &CommitEntry) -> bool {
144        self.path == other.path
145    }
146}
147
148impl Eq for CommitEntry {}
149impl Hash for CommitEntry {
150    fn hash<H: Hasher>(&self, state: &mut H) {
151        self.path.hash(state);
152    }
153}
154
155impl CommitEntry {
156    // For HashSet search purposes
157    pub fn from_path<T: AsRef<Path>>(path: T) -> CommitEntry {
158        CommitEntry {
159            commit_id: String::from(""),
160            path: path.as_ref().to_path_buf(),
161            hash: String::from(""),
162            num_bytes: 0,
163            last_modified_seconds: 0,
164            last_modified_nanoseconds: 0,
165        }
166    }
167
168    pub fn from_file_node(file_node: &FileNode) -> CommitEntry {
169        CommitEntry {
170            commit_id: file_node.last_commit_id().to_string(),
171            path: PathBuf::from(file_node.name()),
172            hash: file_node.hash().to_string(),
173            num_bytes: file_node.num_bytes(),
174            last_modified_seconds: file_node.last_modified_seconds(),
175            last_modified_nanoseconds: file_node.last_modified_nanoseconds(),
176        }
177    }
178
179    pub fn from_dir_node(dir_node: &DirNode) -> CommitEntry {
180        CommitEntry {
181            commit_id: dir_node.last_commit_id().to_string(),
182            path: PathBuf::from(dir_node.name()),
183            hash: dir_node.hash().to_string(),
184            num_bytes: dir_node.num_bytes(),
185            last_modified_seconds: dir_node.last_modified_seconds(),
186            last_modified_nanoseconds: dir_node.last_modified_nanoseconds(),
187        }
188    }
189
190    pub fn version_file(&self) -> PathBuf {
191        let current_dir = env::current_dir().unwrap();
192        let repo_dir = util::fs::get_repo_root(&current_dir).expect("Oxen repo not found.");
193        let repo = LocalRepository::from_dir(&repo_dir).unwrap();
194        util::fs::version_path(&repo, self)
195    }
196
197    // <= 0.8.4:
198    pub fn deprecated_filename(&self) -> PathBuf {
199        PathBuf::from(format!("{}.{}", self.commit_id, self.extension()))
200    }
201
202    pub fn filename(&self) -> PathBuf {
203        if self.extension() == "" {
204            PathBuf::from(VERSION_FILE_NAME)
205        } else {
206            PathBuf::from(format!("{}.{}", VERSION_FILE_NAME, self.extension()))
207        }
208    }
209
210    pub fn filename_from_commit_id(&self, commit_id: &str) -> PathBuf {
211        PathBuf::from(format!("{}.{}", commit_id, self.extension()))
212    }
213
214    pub fn extension(&self) -> String {
215        if let Some(ext) = self.path.extension() {
216            String::from(ext.to_str().unwrap_or(""))
217        } else {
218            String::from("")
219        }
220    }
221
222    pub fn to_synced(&self) -> CommitEntry {
223        CommitEntry {
224            commit_id: self.commit_id.to_owned(),
225            path: self.path.to_owned(),
226            hash: self.hash.to_owned(),
227            num_bytes: self.num_bytes,
228            last_modified_seconds: self.last_modified_seconds,
229            last_modified_nanoseconds: self.last_modified_nanoseconds,
230        }
231    }
232
233    pub fn to_remote(&self) -> RemoteEntry {
234        RemoteEntry {
235            filename: self.path.to_str().unwrap_or("").to_string(),
236            hash: self.hash.to_owned(),
237        }
238    }
239
240    pub fn to_uri_encoded(&self) -> String {
241        serde_url_params::to_string(&self).unwrap()
242    }
243
244    pub fn has_different_modification_time(&self, time: &FileTime) -> bool {
245        self.last_modified_nanoseconds != time.nanoseconds()
246            || self.last_modified_seconds != time.unix_seconds()
247    }
248}