Index parsing
This commit is contained in:
@@ -1,23 +1,77 @@
|
|||||||
use std::{
|
use std::{
|
||||||
io::{
|
io::{
|
||||||
self,
|
self,
|
||||||
|
prelude::*,
|
||||||
Error,
|
Error,
|
||||||
},
|
},
|
||||||
|
fs::File,
|
||||||
|
fmt::Display,
|
||||||
|
path::Path,
|
||||||
};
|
};
|
||||||
|
use std::ffi::CStr;
|
||||||
|
use sha1::{Sha1, Digest};
|
||||||
|
|
||||||
use crate::GIT_DIR;
|
use crate::GIT_DIR;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ObjectType {
|
||||||
|
Regular,
|
||||||
|
SymLink,
|
||||||
|
GitLink,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ObjectType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match &self {
|
||||||
|
Self::Regular => write!(f, "Regular"),
|
||||||
|
Self::SymLink => write!(f, "Symlink"),
|
||||||
|
Self::GitLink => write!(f, "Gitlink"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct IndexEntry {
|
pub struct IndexEntry {
|
||||||
file_size: u32,
|
entry_size: usize,
|
||||||
|
object_type: ObjectType,
|
||||||
|
permissions: u16,
|
||||||
hash: [u8; 20],
|
hash: [u8; 20],
|
||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexEntry {
|
impl IndexEntry {
|
||||||
fn from_bytes(bytes: Vec<u8>) -> io::Result<Self> {
|
fn from_bytes(bytes: &[u8]) -> io::Result<Self> {
|
||||||
|
let mode = &bytes[24..28];
|
||||||
|
let ot_bin = mode[2] >> 4;
|
||||||
|
let object_type = match ot_bin {
|
||||||
|
0b1000 => ObjectType::Regular,
|
||||||
|
0b1010 => ObjectType::SymLink,
|
||||||
|
0b1110 => ObjectType::GitLink,
|
||||||
|
_ => return Err(Error::other(format!("Invalid object type: {}", ot_bin)))
|
||||||
|
};
|
||||||
|
let permissions = u16::from_be_bytes(mode[2..4].try_into().unwrap()) &!0xFE00;
|
||||||
|
match (permissions, &object_type) {
|
||||||
|
(0, ObjectType::GitLink) => (),
|
||||||
|
(0, ObjectType::SymLink) => (),
|
||||||
|
(0o755, ObjectType::Regular) => (),
|
||||||
|
(0o644, ObjectType::Regular) => (),
|
||||||
|
_ => return Err(Error::other(format!("Invalid permissions (0o{:o}) for type {}", permissions, object_type)))
|
||||||
|
};
|
||||||
|
|
||||||
return Err(Error::other("Not implemented"))
|
let hash: [u8; 20] = bytes[40..60].try_into().unwrap();
|
||||||
|
let cname = CStr::from_bytes_until_nul(&bytes[62..]).unwrap();
|
||||||
|
let name = String::from(cname.to_str().unwrap());
|
||||||
|
|
||||||
|
let entry_size = usize::div_ceil(62 + cname.count_bytes(), 8) * 8;
|
||||||
|
|
||||||
|
Ok(IndexEntry { entry_size, object_type, permissions, hash, name })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_bytes(&self) -> io::Result<Vec<u8>> {
|
||||||
|
let bytes = Vec::new();
|
||||||
|
|
||||||
|
Ok(bytes)
|
||||||
|
// Err(Error::other("Not implemented"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,29 +82,56 @@ pub struct Index {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Index {
|
impl Index {
|
||||||
pub fn from_bytes(bytes: Vec<u8>) -> io::Result<Self> {
|
pub fn load() -> io::Result<Self> {
|
||||||
let magic: [u8; 4] = match bytes.first_chunk() {
|
let path = Path::new(GIT_DIR)
|
||||||
None => return Err(Error::other("Error parsing index")),
|
.join("index");
|
||||||
Some(magic) => *magic,
|
let mut file = File::open(&path)?;
|
||||||
|
let mut content: Vec<u8> = Vec::new();
|
||||||
|
file.read_to_end(&mut content)?;
|
||||||
|
|
||||||
|
Index::from_bytes(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_bytes(bytes: Vec<u8>) -> io::Result<Self> {
|
||||||
|
match &bytes[0..4] {
|
||||||
|
b"DIRC" => (),
|
||||||
|
_ => return Err(Error::other("Invalid index signature"))
|
||||||
};
|
};
|
||||||
|
|
||||||
match str::from_utf8(&magic) {
|
let version = u32::from_be_bytes(bytes[4..8].try_into().unwrap());
|
||||||
Ok("DIRC") => (),
|
let count = u32::from_be_bytes(bytes[8..12].try_into().unwrap());
|
||||||
_ => return Err(Error::other("Invalid index"))
|
|
||||||
|
let mut entries: Vec<IndexEntry> = Vec::with_capacity(usize::try_from(count).unwrap());
|
||||||
|
|
||||||
|
let mut offset = 12;
|
||||||
|
|
||||||
|
for _i in 0..count {
|
||||||
|
let entry = IndexEntry::from_bytes(&bytes[offset..])?;
|
||||||
|
offset += entry.entry_size;
|
||||||
|
entries.push(entry);
|
||||||
};
|
};
|
||||||
|
|
||||||
let version_bytes = <[u8; 4]>::try_from(&bytes.as_slice()[4..8]).unwrap();
|
let mut hasher = Sha1::new();
|
||||||
let version = u32::from_be_bytes(version_bytes);
|
hasher.update(&bytes[..offset]);
|
||||||
|
let hash = hasher.finalize();
|
||||||
let count_bytes = <[u8; 4]>::try_from(&bytes.as_slice()[8..12]).unwrap();
|
|
||||||
let count = u32::from_be_bytes(count_bytes);
|
|
||||||
|
|
||||||
let entries: Vec<IndexEntry> = Vec::with_capacity(usize::try_from(count).unwrap());
|
|
||||||
|
|
||||||
for i in 0..=count {
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
if bytes[offset..] != *hash {
|
||||||
|
return Err(Error::other("Invalid index"));
|
||||||
|
}
|
||||||
return Ok(Index { version, entries});
|
return Ok(Index { version, entries});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_bytes(&self) -> io::Result<Vec<u8>> {
|
||||||
|
let mut bytes: Vec<u8> = Vec::new();
|
||||||
|
bytes.extend("DIRC".as_bytes());
|
||||||
|
bytes.extend(self.version.to_be_bytes());
|
||||||
|
let count: u32 = self.entries.len() as u32;
|
||||||
|
bytes.extend(count.to_be_bytes());
|
||||||
|
|
||||||
|
for entry in &self.entries {
|
||||||
|
bytes.extend(entry.to_bytes()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(bytes)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use std::fs;
|
use std::{
|
||||||
|
fs::{File},
|
||||||
|
io::prelude::*,
|
||||||
|
path::Path
|
||||||
|
};
|
||||||
use crate::subcommands::Subcommand;
|
use crate::subcommands::Subcommand;
|
||||||
|
use crate::git_fs::index::Index;
|
||||||
|
|
||||||
use super::CmdResult;
|
use super::CmdResult;
|
||||||
|
|
||||||
@@ -11,18 +16,13 @@ pub struct TestSubcommand {
|
|||||||
|
|
||||||
impl Subcommand for TestSubcommand {
|
impl Subcommand for TestSubcommand {
|
||||||
fn run(&self) -> CmdResult {
|
fn run(&self) -> CmdResult {
|
||||||
let metadata = match fs::metadata(self.path.clone()) {
|
let index = Index::load().unwrap();
|
||||||
Ok(metadata) => metadata,
|
|
||||||
_ => return Err(String::from("")),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mtime = match metadata.modified() {
|
println!("{index:?}");
|
||||||
Ok(mtime) => mtime,
|
|
||||||
_ => return Err(String::from("")),
|
|
||||||
};
|
|
||||||
|
|
||||||
// mtime
|
let path = Path::new(&self.path);
|
||||||
println!("{mtime:?}");
|
let mut file = File::create(path).unwrap();
|
||||||
|
let _ = file.write_all(&index.to_bytes().unwrap());
|
||||||
|
|
||||||
Ok(String::from(""))
|
Ok(String::from(""))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user