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
71impl 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#[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
141impl 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 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(¤t_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 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}