Skip to content

Commit

Permalink
feat(ref): oh god, implement refs and ref sets
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisdickinson committed Dec 30, 2018
1 parent c60cf25 commit 6157317
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod errors;
pub mod stores;
pub mod objects;
pub mod packindex;
pub mod refs;

#[cfg(test)]
mod tests {
Expand Down
137 changes: 137 additions & 0 deletions src/refs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::io::Read;

use crate::id::Id;

#[derive(Copy, Clone)]
enum Kind {
Local,
Remote,
Tag
}

enum RefPtr {
Indirect(String),
Direct(Id)
}

struct Ref {
kind: Kind,
ptr: RefPtr
}

impl Ref {
pub fn load(path: &Path, kind: Kind) -> Result<Ref, std::io::Error> {
let mut f = File::open(path)?;
let mut buffer = Vec::new();
f.read_to_end(&mut buffer)?;

if let Ok(contents) = std::str::from_utf8(&buffer) {
if contents.len() < 5 {
return Err(std::io::ErrorKind::InvalidData.into());
}

if &contents[0..5] == "ref: " {
return Ok(Ref {
kind,
ptr: RefPtr::Indirect(String::from(&contents[6..]))
});
}

if buffer.len() == 40 {
if let Some(id) = Id::from_str(contents) {
return Ok(Ref {
ptr: RefPtr::Direct(id),
kind
});
}
}
}

return Err(std::io::ErrorKind::InvalidData.into());
}
}

struct RefSet(HashMap<String, Ref>);

fn recurse_dir<'a>(
root: &mut PathBuf,
dirs: &mut Vec<String>,
map: &mut HashMap<String, Ref>,
k: Kind
) -> Result<(), std::io::Error> {
for entry in std::fs::read_dir(root.as_path())? {
let entry = entry?;

let typ = entry.file_type()?;
let os_filename = entry.file_name();
let opt_filename = os_filename.to_str();
if opt_filename.is_none() {
continue
}
let filename = opt_filename.unwrap();

dirs.push(String::from(filename));
if typ.is_dir() {
recurse_dir(&mut entry.path(), dirs, map, k)?;
} else {
let ref_name: String = dirs.join("/");
if let Ok(reference) = Ref::load(&entry.path(), k) {
map.insert(ref_name, reference);
}
}
dirs.pop();
}

Ok(())
}

impl RefSet {
pub fn from_path(path: &Path) -> Result<RefSet, std::io::Error> {
let mut root = std::path::PathBuf::new();
let mut map = HashMap::new();
let mut dirs = Vec::new();
root.push(path);
root.push(".git");
root.push("refs");
root.push("heads");
recurse_dir(&mut root, &mut dirs, &mut map, Kind::Local)?;
root.pop();
root.push("remotes");
recurse_dir(&mut root, &mut dirs, &mut map, Kind::Remote)?;
root.pop();
root.push("tags");
recurse_dir(&mut root, &mut dirs, &mut map, Kind::Tag)?;
root.pop();
root.pop();
root.push("HEAD");
if let Ok(reference) = Ref::load(root.as_path(), Kind::Local) {
map.insert(String::from("HEAD"), reference);
};

Ok(RefSet {
0: map
})
}

pub fn deref(&self, name: &str) -> Option<&Id> {
let mut reference = self.0.get(name);
loop {
match reference {
Some(xs) => {
match xs.ptr {
RefPtr::Direct(ref id) => return Some(&id),
RefPtr::Indirect(ref string) => {
reference = self.0.get(string.as_str());
}
}
},
None => return None
}
}

None
}
}

0 comments on commit 6157317

Please sign in to comment.