-
Notifications
You must be signed in to change notification settings - Fork 0
/
filesystem.rs
143 lines (130 loc) · 4.71 KB
/
filesystem.rs
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
use flate2::Compression;
use flate2::{read::ZlibDecoder, write::ZlibEncoder};
use itertools::Itertools;
use std::io::prelude::*;
use std::{
env::current_dir,
fs::{create_dir, write},
path::PathBuf,
};
use super::objects::{OgitObject, OgitObjectType};
const OGIT_DIR: &str = ".ogit";
/// Initializes the filesystem resources for ogit to operate
pub fn ogit_init() -> std::io::Result<()> {
// fine here to panic if Err, don't have permission in this location list directory
let mut new_ogit_dir = current_dir()?;
new_ogit_dir.push(PathBuf::from(OGIT_DIR));
let mut ogit_obj_database_dir = new_ogit_dir.clone();
ogit_obj_database_dir.push("objects");
// raise error around unable to create directory.
create_dir(new_ogit_dir)?;
create_dir(ogit_obj_database_dir)?;
Ok(())
}
pub fn hash_object(
data: &str,
object_type: Option<OgitObjectType>,
) -> Result<OgitObject, std::io::Error> {
let object = OgitObject::new(data.as_bytes(), object_type.unwrap_or(OgitObjectType::Blob));
// write object to database
let mut object_path = match current_dir() {
Ok(p) => p,
Err(e) => {
eprintln!("Error getting current directory: {e}");
return Err(e);
}
};
object_path.push(PathBuf::from(OGIT_DIR.to_string()));
object_path.push(PathBuf::from("objects"));
// this result will return a 2 character directory and the rest of the hash
let object_file_path = object.object_database_filepath();
let (dir, file) = object_file_path.split('/').collect_tuple().unwrap();
object_path.push(PathBuf::from(dir));
// check if the directory for the object exists, if not create it
if !object_path.exists() {
match create_dir(object_path.clone()) {
Ok(()) => (),
Err(e) => {
eprintln!(
"Error creating new Object directory: {}",
object_path.display()
);
return Err(e);
}
}
}
object_path.push(file);
// write and catch the possible IO error
// compress the data before writing to the file
let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
encoder.write_all(object.file_content().as_bytes())?;
let compressed_bytes = encoder.finish().unwrap();
// write the compressed data to the file
write(object_path, compressed_bytes)?;
Ok(object)
}
pub fn get_object(
object_id: &str,
expected_object_type: Option<OgitObjectType>,
) -> Result<OgitObject, std::io::Error> {
let mut object_path = match current_dir() {
Ok(p) => p,
Err(e) => {
eprintln!("Error getting current directory: {e}");
return Err(e);
}
};
object_path.push(PathBuf::from(OGIT_DIR.to_string()));
object_path.push(PathBuf::from("objects"));
let (dir, file) = object_id.split_at(2);
object_path.push(PathBuf::from(dir));
object_path.push(PathBuf::from(file));
// TODO: move this into `OgitObject::from_database`
let object_data = match std::fs::read(object_path) {
Ok(data) => {
let mut output = Vec::new();
let mut decoder = ZlibDecoder::new(&data[..]);
decoder.read_to_end(&mut output)?;
output
}
Err(e) => {
eprintln!("Error reading object file: {e}");
return Err(e);
}
};
let object = OgitObject::from_database(&object_data);
if let Some(ogit_type) = expected_object_type {
if object.variant != ogit_type {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"Object type mismatch, expected: {ogit_type}, got: {}",
object.variant
),
));
}
}
Ok(object)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_object() {
let _ = ogit_init();
let object = hash_object("hello world", None).unwrap();
assert_eq!(object.variant, OgitObjectType::Blob);
assert_eq!(object.data, "hello world".as_bytes());
assert_eq!(object.file_content(), "blob 11\0hello world");
let hex_string = object.hex_string();
let path = object.object_database_filepath();
assert_eq!(path, format!("{}/{}", &hex_string[..2], &hex_string[2..]));
let mut object_path = PathBuf::from(OGIT_DIR.to_string());
object_path.push(PathBuf::from("objects"));
let (dir, file) = path.split('/').collect_tuple().unwrap();
object_path.push(PathBuf::from(dir));
object_path.push(PathBuf::from(file));
assert!(object_path.exists());
std::fs::remove_file(object_path).unwrap();
}
}