Skip to content

Commit

Permalink
Get objects from object packs (#191)
Browse files Browse the repository at this point in the history
  • Loading branch information
hateofhades authored Nov 20, 2024
1 parent 765283a commit 884c808
Show file tree
Hide file tree
Showing 14 changed files with 855 additions and 36 deletions.
1 change: 1 addition & 0 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ sha-1 = "0.10.1"
tauri-plugin-shell = "2"
tauri-plugin-dialog = "2"
tauri-plugin-process = "2"
hex = "0.4.3"

[features]
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/errors/git_object_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub enum GitObjectError {
ParsingError,
ShaError,
InvalidHash,
PackError,
}

#[derive(Debug, PartialEq)]
Expand Down
38 changes: 31 additions & 7 deletions src-tauri/src/git/git_blob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,23 @@ impl GitObject for GitBlob {
*
* Returns the GitBlob
*/
fn from_encoded_data(encoded_data: &[u8]) -> Result<Self, GitObjectError> {
let decoded_data = Self::decode_data(encoded_data)?;
let (data, size) = Self::check_header_valid_and_get_data(&decoded_data)?;
fn from_encoded_data(
encoded_data: &[u8],
needs_decoding: bool,
) -> Result<Self, GitObjectError> {
// Decode the data and check if the header is valid
let decoded_data = if needs_decoding {
Self::decode_data(encoded_data)?
} else {
String::from_utf8_lossy(encoded_data).to_string()
};

let (data, size) = if needs_decoding {
Self::check_header_valid_and_get_data(&decoded_data)?
} else {
(decoded_data.as_str(), decoded_data.len())
};

let (data, _) = data.split_once("\n").ok_or(GitObjectError::ParsingError)?;

Ok(Self::new(size, data.as_bytes().to_vec()))
Expand Down Expand Up @@ -104,7 +118,7 @@ mod tests {
let data = String::from("test");
let encoded_data = create_encoded_blob_file(Some(data.clone())).unwrap();

let blob = GitBlob::from_encoded_data(encoded_data.as_slice()).unwrap();
let blob = GitBlob::from_encoded_data(encoded_data.as_slice(), true).unwrap();

assert_eq!(blob.get_hash(), "9daeafb9864cf43055ae93beb0afd6c7d144bfa4");
}
Expand All @@ -114,15 +128,15 @@ mod tests {
let data = String::from("test");
let encoded_data = create_encoded_blob_file(Some(data.clone())).unwrap();

let blob = GitBlob::from_encoded_data(encoded_data.as_slice()).unwrap();
let blob = GitBlob::from_encoded_data(encoded_data.as_slice(), true).unwrap();

assert_eq!(blob.size(), data.len());
assert_eq!(blob.data(), data.as_bytes());
}

#[test]
fn test_git_blob_from_encoded_data_invalid_blob_file() {
let result = GitBlob::from_encoded_data(vec![0, 1, 2, 3, 4, 5].as_slice());
let result = GitBlob::from_encoded_data(vec![0, 1, 2, 3, 4, 5].as_slice(), true);
assert_eq!(result, Err(GitObjectError::DecompressionError));
}

Expand All @@ -138,7 +152,7 @@ mod tests {
let mut encoded_file_content = Vec::new();
zlib.read_to_end(&mut encoded_file_content).unwrap();

let result = GitBlob::from_encoded_data(encoded_file_content.as_slice());
let result = GitBlob::from_encoded_data(encoded_file_content.as_slice(), true);
assert_eq!(
result,
Err(GitObjectError::InvalidObjectFile(
Expand All @@ -155,6 +169,16 @@ mod tests {
assert_eq!(blob.data(), data.as_slice());
}

#[test]
fn test_already_decoded_data() {
let data = vec![1, 2, 3, 4, 5];
let blob = GitBlob::new(data.len(), data.clone());
let decoded_data = blob.get_data_string() + "\n";

let git_blob = GitBlob::from_encoded_data(decoded_data.as_bytes(), false).unwrap();
assert_eq!(git_blob.get_hash(), blob.get_hash());
}

#[test]
fn test_git_blob_serialization() {
let data = vec![1, 2, 3, 4, 5];
Expand Down
52 changes: 38 additions & 14 deletions src-tauri/src/git/git_commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,22 @@ impl GitObject for GitCommit {
*
* Returns the GitCommit
*/
fn from_encoded_data(encoded_data: &[u8]) -> Result<Self, GitObjectError> {
let decoded_data = Self::decode_data(encoded_data)?;
let (data, _) = Self::check_header_valid_and_get_data(&decoded_data)?;
fn from_encoded_data(
encoded_data: &[u8],
needs_decoding: bool,
) -> Result<Self, GitObjectError> {
// Decode the data and check if the header is valid
let decoded_data = if needs_decoding {
Self::decode_data(encoded_data)?
} else {
String::from_utf8_lossy(encoded_data).to_string()
};

let data = if needs_decoding {
Self::check_header_valid_and_get_data(&decoded_data)?.0
} else {
&decoded_data
};

// The data must contain a tree hash, either an author or committer,
// none or more parent commits, and a message
Expand All @@ -171,7 +184,7 @@ impl GitObject for GitCommit {
let mut committer = Option::<GitCommitAuthor>::None;
let mut in_signature = false;
let mut signature = String::new();

// Remove the last newline character
let mut data = &data[..data.len() - 1];
while !data.is_empty() {
Expand Down Expand Up @@ -322,11 +335,13 @@ mod tests {

fn mock_git_commit() -> GitCommit {
let author = mock_git_commit_author();
let committer = mock_git_commit_committer();

GitCommit::new(
"tree_hash",
&["parent_hash1".to_string(), "parent_hash2".to_string()],
author.clone(),
author.clone(),
author,
committer,
"commit message",
None,
)
Expand Down Expand Up @@ -395,7 +410,7 @@ mod tests {
)
.unwrap();

let git_commit = GitCommit::from_encoded_data(&encoded_file_content).unwrap();
let git_commit = GitCommit::from_encoded_data(&encoded_file_content, true).unwrap();
assert_eq!(*git_commit.get_hash(), commit_hash);
assert_eq!(*git_commit.get_parent_hashes(), Vec::<String>::new());
assert_eq!(
Expand All @@ -411,7 +426,7 @@ mod tests {
fn test_from_string_invalid() {
let encoded_file_content = "invalid content".as_bytes();

let git_commit = GitCommit::from_encoded_data(encoded_file_content);
let git_commit = GitCommit::from_encoded_data(encoded_file_content, true);
assert!(git_commit.is_err());
}

Expand All @@ -428,7 +443,7 @@ mod tests {
);

let git_commit =
GitCommit::from_encoded_data(encoded_file_content.as_ref().unwrap()).unwrap();
GitCommit::from_encoded_data(encoded_file_content.as_ref().unwrap(), true).unwrap();

assert_eq!(
git_commit.get_encoded_data().unwrap(),
Expand All @@ -453,7 +468,7 @@ mod tests {
);

let git_commit =
GitCommit::from_encoded_data(encoded_file_content.as_ref().unwrap()).unwrap();
GitCommit::from_encoded_data(encoded_file_content.as_ref().unwrap(), true).unwrap();

assert_eq!(
git_commit.get_encoded_data().unwrap(),
Expand All @@ -477,7 +492,7 @@ mod tests {
);

let encoded_file_content = git_commit.get_encoded_data().unwrap();
let git_commit = GitCommit::from_encoded_data(&encoded_file_content).unwrap();
let git_commit = GitCommit::from_encoded_data(&encoded_file_content, true).unwrap();

assert_eq!(
git_commit.get_gpg_signature().clone().unwrap(),
Expand Down Expand Up @@ -505,25 +520,34 @@ mod tests {
);

let git_commit =
GitCommit::from_encoded_data(encoded_file_content.as_ref().unwrap()).unwrap();
GitCommit::from_encoded_data(encoded_file_content.as_ref().unwrap(), true).unwrap();
assert_eq!(git_commit.get_hash(), commit_hash);
assert_eq!(git_commit.parent_hashes, parent_commit_hash);
assert_eq!(git_commit.tree_hash, tree_hash);
assert_eq!(git_commit.message, "test commit");
assert_eq!(git_commit.author, committer);
}

#[test]
fn test_already_decoded_data() {
let commit = mock_git_commit();
let decoded_data = commit.get_data_string() + "\n";

let git_commit = GitCommit::from_encoded_data(decoded_data.as_bytes(), false).unwrap();
assert_eq!(git_commit.get_hash(), commit.get_hash());
}

#[test]
fn test_serialize_git_commit() {
let git_commit = mock_git_commit();
let serialized = serde_json::to_string(&git_commit).unwrap();
let expected = r#"{"tree_hash":"tree_hash","parent_hashes":["parent_hash1","parent_hash2"],"author":{"user":{"name":"Test User","email":"[email protected]"},"date_seconds":1234567890,"timezone":"+0000","type_":"Author"},"committer":{"user":{"name":"Test User","email":"[email protected]"},"date_seconds":1234567890,"timezone":"+0000","type_":"Author"},"message":"commit message","gpg_signature":null}"#;
let expected = r#"{"tree_hash":"tree_hash","parent_hashes":["parent_hash1","parent_hash2"],"author":{"user":{"name":"Test User","email":"[email protected]"},"date_seconds":1234567890,"timezone":"+0000","type_":"Author"},"committer":{"user":{"name":"Test User","email":"[email protected]"},"date_seconds":1234567890,"timezone":"+0000","type_":"Committer"},"message":"commit message","gpg_signature":null}"#;
assert_eq!(serialized, expected);
}

#[test]
fn test_deserialize_git_commit() {
let json_str = r#"{"tree_hash":"tree_hash","parent_hashes":["parent_hash1","parent_hash2"],"author":{"user":{"name":"Test User","email":"[email protected]"},"date_seconds":1234567890,"timezone":"+0000","type_":"Author"},"committer":{"user":{"name":"Test User","email":"[email protected]"},"date_seconds":1234567890,"timezone":"+0000","type_":"Author"},"message":"commit message","gpg_signature":null}"#;
let json_str = r#"{"tree_hash":"tree_hash","parent_hashes":["parent_hash1","parent_hash2"],"author":{"user":{"name":"Test User","email":"[email protected]"},"date_seconds":1234567890,"timezone":"+0000","type_":"Author"},"committer":{"user":{"name":"Test User","email":"[email protected]"},"date_seconds":1234567890,"timezone":"+0000","type_":"Committer"},"message":"commit message","gpg_signature":null}"#;
let deserialized: GitCommit = serde_json::from_str(json_str).unwrap();
let expected = mock_git_commit();
assert_eq!(deserialized, expected);
Expand Down
32 changes: 32 additions & 0 deletions src-tauri/src/git/git_folders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ impl fmt::Display for GitFolders {
}
}

pub enum GitObjects {
INFO,
PACK,
}

impl AsRef<str> for GitObjects {
fn as_ref(&self) -> &str {
match *self {
GitObjects::INFO => "info",
GitObjects::PACK => "pack",
}
}
}

#[derive(EnumIter, Serialize, Deserialize, Debug, Clone, PartialEq)]
pub enum GitRefs {
HEADS,
Expand Down Expand Up @@ -77,3 +91,21 @@ pub enum GitBranchType {
Remote(String),
Tags,
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_git_folders_as_ref() {
assert_eq!(GitFolders::REFS.as_ref(), "refs");
assert_eq!(GitFolders::OBJECTS.as_ref(), "objects");
assert_eq!(GitFolders::HOOKS.as_ref(), "hooks");
}

#[test]
fn test_git_objects_as_ref() {
assert_eq!(GitObjects::INFO.as_ref(), "info");
assert_eq!(GitObjects::PACK.as_ref(), "pack");
}
}
32 changes: 27 additions & 5 deletions src-tauri/src/git/git_tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,22 @@ impl GitObject for GitTag {
*
* Returns the GitTag if the encoded data is valid, otherwise an error
*/
fn from_encoded_data(encoded_data: &[u8]) -> Result<Self, GitObjectError> {
let decoded_data = Self::decode_data(encoded_data)?;
let (data, _) = Self::check_header_valid_and_get_data(&decoded_data)?;
fn from_encoded_data(
encoded_data: &[u8],
needs_decoding: bool,
) -> Result<Self, GitObjectError> {
// Decode the data and check if the header is valid
let decoded_data = if needs_decoding {
Self::decode_data(encoded_data)?
} else {
String::from_utf8_lossy(encoded_data).to_string()
};

let data = if needs_decoding {
Self::check_header_valid_and_get_data(&decoded_data)?.0
} else {
&decoded_data
};

// The data must contain an object hash, a type, a tag name, a tagger and a message
let mut object = "";
Expand Down Expand Up @@ -238,7 +251,7 @@ mod tests {
)
.unwrap();

let git_tag = GitTag::from_encoded_data(&encoded).unwrap();
let git_tag = GitTag::from_encoded_data(&encoded, true).unwrap();
assert!(git_tag.get_object_hash() == "25723a3e66cd8dcbaf085ed83b86a8007df7ff32");
assert!(git_tag.get_type_() == "commit");
assert!(git_tag.get_tag_name() == "test");
Expand All @@ -251,7 +264,7 @@ mod tests {
fn test_from_string_invalid() {
let encoded_file_content = "invalid content".as_bytes();

let git_tag = GitTag::from_encoded_data(encoded_file_content);
let git_tag = GitTag::from_encoded_data(encoded_file_content, true);
assert!(git_tag.is_err());
}

Expand All @@ -263,6 +276,15 @@ mod tests {
assert!(git_tag.to_string() == expected);
}

#[test]
fn test_already_decoded_data() {
let tag = mock_git_tag();
let decoded_data = tag.get_data_string() + "\n";

let git_tag = GitTag::from_encoded_data(decoded_data.as_bytes(), false).unwrap();
assert_eq!(git_tag.get_hash(), tag.get_hash());
}

#[test]
fn test_serialize() {
let git_tag = mock_git_tag();
Expand Down
Loading

0 comments on commit 884c808

Please sign in to comment.