Skip to content

Commit 6157317

Browse files
feat(ref): oh god, implement refs and ref sets
1 parent c60cf25 commit 6157317

File tree

2 files changed

+138
-0
lines changed

2 files changed

+138
-0
lines changed

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub mod errors;
77
pub mod stores;
88
pub mod objects;
99
pub mod packindex;
10+
pub mod refs;
1011

1112
#[cfg(test)]
1213
mod tests {

src/refs.rs

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
use std::collections::HashMap;
2+
use std::path::{Path, PathBuf};
3+
use std::fs::File;
4+
use std::io::Read;
5+
6+
use crate::id::Id;
7+
8+
#[derive(Copy, Clone)]
9+
enum Kind {
10+
Local,
11+
Remote,
12+
Tag
13+
}
14+
15+
enum RefPtr {
16+
Indirect(String),
17+
Direct(Id)
18+
}
19+
20+
struct Ref {
21+
kind: Kind,
22+
ptr: RefPtr
23+
}
24+
25+
impl Ref {
26+
pub fn load(path: &Path, kind: Kind) -> Result<Ref, std::io::Error> {
27+
let mut f = File::open(path)?;
28+
let mut buffer = Vec::new();
29+
f.read_to_end(&mut buffer)?;
30+
31+
if let Ok(contents) = std::str::from_utf8(&buffer) {
32+
if contents.len() < 5 {
33+
return Err(std::io::ErrorKind::InvalidData.into());
34+
}
35+
36+
if &contents[0..5] == "ref: " {
37+
return Ok(Ref {
38+
kind,
39+
ptr: RefPtr::Indirect(String::from(&contents[6..]))
40+
});
41+
}
42+
43+
if buffer.len() == 40 {
44+
if let Some(id) = Id::from_str(contents) {
45+
return Ok(Ref {
46+
ptr: RefPtr::Direct(id),
47+
kind
48+
});
49+
}
50+
}
51+
}
52+
53+
return Err(std::io::ErrorKind::InvalidData.into());
54+
}
55+
}
56+
57+
struct RefSet(HashMap<String, Ref>);
58+
59+
fn recurse_dir<'a>(
60+
root: &mut PathBuf,
61+
dirs: &mut Vec<String>,
62+
map: &mut HashMap<String, Ref>,
63+
k: Kind
64+
) -> Result<(), std::io::Error> {
65+
for entry in std::fs::read_dir(root.as_path())? {
66+
let entry = entry?;
67+
68+
let typ = entry.file_type()?;
69+
let os_filename = entry.file_name();
70+
let opt_filename = os_filename.to_str();
71+
if opt_filename.is_none() {
72+
continue
73+
}
74+
let filename = opt_filename.unwrap();
75+
76+
dirs.push(String::from(filename));
77+
if typ.is_dir() {
78+
recurse_dir(&mut entry.path(), dirs, map, k)?;
79+
} else {
80+
let ref_name: String = dirs.join("/");
81+
if let Ok(reference) = Ref::load(&entry.path(), k) {
82+
map.insert(ref_name, reference);
83+
}
84+
}
85+
dirs.pop();
86+
}
87+
88+
Ok(())
89+
}
90+
91+
impl RefSet {
92+
pub fn from_path(path: &Path) -> Result<RefSet, std::io::Error> {
93+
let mut root = std::path::PathBuf::new();
94+
let mut map = HashMap::new();
95+
let mut dirs = Vec::new();
96+
root.push(path);
97+
root.push(".git");
98+
root.push("refs");
99+
root.push("heads");
100+
recurse_dir(&mut root, &mut dirs, &mut map, Kind::Local)?;
101+
root.pop();
102+
root.push("remotes");
103+
recurse_dir(&mut root, &mut dirs, &mut map, Kind::Remote)?;
104+
root.pop();
105+
root.push("tags");
106+
recurse_dir(&mut root, &mut dirs, &mut map, Kind::Tag)?;
107+
root.pop();
108+
root.pop();
109+
root.push("HEAD");
110+
if let Ok(reference) = Ref::load(root.as_path(), Kind::Local) {
111+
map.insert(String::from("HEAD"), reference);
112+
};
113+
114+
Ok(RefSet {
115+
0: map
116+
})
117+
}
118+
119+
pub fn deref(&self, name: &str) -> Option<&Id> {
120+
let mut reference = self.0.get(name);
121+
loop {
122+
match reference {
123+
Some(xs) => {
124+
match xs.ptr {
125+
RefPtr::Direct(ref id) => return Some(&id),
126+
RefPtr::Indirect(ref string) => {
127+
reference = self.0.get(string.as_str());
128+
}
129+
}
130+
},
131+
None => return None
132+
}
133+
}
134+
135+
None
136+
}
137+
}

0 commit comments

Comments
 (0)