Initial commit
This commit is contained in:
commit
88fd274d0c
11 changed files with 2403 additions and 0 deletions
134
src/repo.rs
Normal file
134
src/repo.rs
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
use crate::{
|
||||
api_client::{MAX_PATH_SIZE, MAX_URL_SIZE},
|
||||
config::Config,
|
||||
};
|
||||
|
||||
use std::{
|
||||
io::{ErrorKind, Read},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
const REPO_METADATA_FILE_NAME: &str = "meta.bin";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RepoMetadata {
|
||||
pub date: u64,
|
||||
pub commit_url: String,
|
||||
content: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ReadRepoMetadataError {
|
||||
CannotOpenFile(std::io::Error),
|
||||
CannotReadFile(std::io::Error),
|
||||
InvalidFormat,
|
||||
UnsupportedVersion,
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for ReadRepoMetadataError {
|
||||
fn from(value: std::io::Error) -> Self {
|
||||
match value.kind() {
|
||||
ErrorKind::UnexpectedEof => Self::InvalidFormat,
|
||||
_ => Self::CannotReadFile(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::string::FromUtf8Error> for ReadRepoMetadataError {
|
||||
fn from(value: std::string::FromUtf8Error) -> Self {
|
||||
Self::InvalidFormat
|
||||
}
|
||||
}
|
||||
|
||||
impl RepoMetadata {
|
||||
pub fn new(config: &Config, repo_dir: &Path) -> Result<Self, ReadRepoMetadataError> {
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.open(repo_dir.join(""))
|
||||
.map_err(ReadRepoMetadataError::CannotOpenFile)?;
|
||||
|
||||
let mut version = [0u8; 1];
|
||||
file.read_exact(&mut version)?;
|
||||
if version != [0] {
|
||||
return Err(ReadRepoMetadataError::UnsupportedVersion);
|
||||
}
|
||||
|
||||
let mut date = [0u8; 8];
|
||||
file.read_exact(&mut date)?;
|
||||
let date = u64::from_be_bytes(date);
|
||||
|
||||
let mut commit_url_len = [0u8; 4];
|
||||
file.read_exact(&mut commit_url_len)?;
|
||||
let commit_url_len = u32::from_be_bytes(commit_url_len) as usize;
|
||||
if commit_url_len > MAX_URL_SIZE {
|
||||
return Err(ReadRepoMetadataError::InvalidFormat);
|
||||
}
|
||||
|
||||
let mut commit_url = vec![0; commit_url_len];
|
||||
file.read_exact(&mut commit_url)?;
|
||||
let commit_url = String::from_utf8(commit_url)?;
|
||||
|
||||
let mut content = Vec::new();
|
||||
file.read_to_end(&mut content)?;
|
||||
|
||||
Ok(Self {
|
||||
date,
|
||||
commit_url,
|
||||
content,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn iter_files<'a>(&'a self) -> RepoMetadataIter<'a> {
|
||||
RepoMetadataIter {
|
||||
repo_metadata: self,
|
||||
offset: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RepoMetadataIter<'a> {
|
||||
repo_metadata: &'a RepoMetadata,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
pub struct RepoMetadataEntry<'a> {
|
||||
file_path: &'a str,
|
||||
hash: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for RepoMetadataIter<'a> {
|
||||
type Item = Result<RepoMetadataEntry<'a>, ReadRepoMetadataError>;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.offset == self.repo_metadata.content.len() {
|
||||
return None;
|
||||
}
|
||||
if self.offset + 4 > self.repo_metadata.content.len() {
|
||||
return Some(Err(ReadRepoMetadataError::InvalidFormat));
|
||||
}
|
||||
let file_path_len = u32::from_be_bytes(
|
||||
self.repo_metadata.content[self.offset..self.offset + 4]
|
||||
.try_into()
|
||||
.expect("unreachable"),
|
||||
) as usize;
|
||||
if file_path_len > MAX_PATH_SIZE {
|
||||
return Some(Err(ReadRepoMetadataError::InvalidFormat));
|
||||
}
|
||||
if self.offset + file_path_len + 48 > self.repo_metadata.content.len() {
|
||||
return Some(Err(ReadRepoMetadataError::InvalidFormat));
|
||||
}
|
||||
|
||||
let Ok(file_path) = str::from_utf8(
|
||||
&self.repo_metadata.content[self.offset + 4..self.offset + 4 + file_path_len],
|
||||
) else {
|
||||
return Some(Err(ReadRepoMetadataError::InvalidFormat));
|
||||
};
|
||||
let Ok(hash) = str::from_utf8(
|
||||
&self.repo_metadata.content
|
||||
[self.offset + 4 + file_path_len..self.offset + 48 + file_path_len],
|
||||
) else {
|
||||
return Some(Err(ReadRepoMetadataError::InvalidFormat));
|
||||
};
|
||||
|
||||
Some(Ok(RepoMetadataEntry { file_path, hash }))
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue