Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions ceres/src/api_service/import_api_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use venus::import_repo::repo::Repo;

use crate::api_service::ApiHandler;
use crate::model::create_file::CreateFileInfo;
use crate::model::publish_path::PublishPathInfo;

#[derive(Clone)]
pub struct ImportApiService {
Expand All @@ -32,6 +33,12 @@ impl ApiHandler for ImportApiService {
));
}

async fn publish_path(&self, _: PublishPathInfo) -> Result<(), GitError> {
return Err(GitError::CustomError(
"Publish operation only support in mono directory".to_string(),
));
}

async fn get_raw_blob_by_hash(&self, hash: &str) -> Result<Option<raw_blob::Model>, MegaError> {
self.context
.services
Expand Down
148 changes: 88 additions & 60 deletions ceres/src/api_service/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ use mercury::{
use venus::monorepo::converter;

use crate::model::{
create_file::CreateFileInfo,
tree::{LatestCommitInfo, TreeBriefItem, TreeCommitItem, UserInfo},
create_file::CreateFileInfo, publish_path::PublishPathInfo, tree::{LatestCommitInfo, TreeBriefItem, TreeCommitItem, UserInfo}
};

pub mod import_api_service;
Expand All @@ -28,6 +27,8 @@ pub mod mono_api_service;
pub trait ApiHandler: Send + Sync {
async fn create_monorepo_file(&self, file_info: CreateFileInfo) -> Result<(), GitError>;

async fn publish_path(&self, publish_info: PublishPathInfo) -> Result<(), GitError>;

async fn get_raw_blob_by_hash(&self, hash: &str) -> Result<Option<raw_blob::Model>, MegaError>;

fn strip_relative(&self, path: &Path) -> Result<PathBuf, GitError>;
Expand Down Expand Up @@ -63,9 +64,9 @@ pub trait ApiHandler: Send + Sync {

async fn get_blob_as_string(&self, file_path: PathBuf) -> Result<String, GitError> {
let filename = file_path.file_name().unwrap().to_str().unwrap();
let path = file_path.parent().unwrap();
let parent = file_path.parent().unwrap();
let mut plain_text = String::new();
if let Some(tree) = self.search_tree_by_path(path).await? {
if let Some(tree) = self.search_tree_by_path(parent).await? {
if let Some(item) = tree.tree_items.into_iter().find(|x| x.name == filename) {
plain_text = match self.get_raw_blob_by_hash(&item.id.to_plain_str()).await {
Ok(Some(model)) => String::from_utf8(model.data.unwrap()).unwrap(),
Expand Down Expand Up @@ -187,18 +188,21 @@ pub trait ApiHandler: Send + Sync {
Ok(res)
}

/// Searches for a tree and affected parent by path.
///
/// This function asynchronously searches for a tree by the provided path.
/// Searches for a tree in the Git repository by its path and returns the trees involved in the update and the target tree.
///
/// # Arguments
///
/// * `path` - A reference to the path to search.
/// * `path` - A reference to the path to search for.
///
/// # Returns
///
/// Returns a tuple containing a vector of parent trees to be updated and
/// the target tree if found, or an error of type `GitError`.
/// A tuple containing:
/// - A vector of trees involved in the update process.
/// - The target tree found at the end of the search.
///
/// # Errors
///
/// Returns a `GitError` if the path does not exist.
async fn search_tree_for_update(&self, path: &Path) -> Result<(Vec<Tree>, Tree), GitError> {
let relative_path = self.strip_relative(path)?;
let root_tree = self.get_root_tree().await;
Expand All @@ -224,14 +228,28 @@ pub trait ApiHandler: Send + Sync {
}
} else {
return Err(GitError::CustomError(
"can't find target parent tree under latest commit".to_string(),
"Path not exist, please create path first!".to_string(),
));
}
}
}
Ok((update_tree, search_tree))
}

/// Searches for a tree by a given path.
///
/// This function takes a `path` and searches for the corresponding tree
/// in the repository. It returns a `Result` containing an `Option<Tree>`.
/// If the tree is found, it returns `Some(Tree)`. If the path does not
/// exist, it returns `None`. In case of an error, it returns a `GitError`.
///
/// # Arguments
///
/// * `path` - A reference to the `Path` to search for the tree.
///
/// # Returns
///
/// * `Result<Option<Tree>, GitError>` - A result containing an optional tree or a Git error.
async fn search_tree_by_path(&self, path: &Path) -> Result<Option<Tree>, GitError> {
let relative_path = self.strip_relative(path)?;
let root_tree = self.get_root_tree().await;
Expand All @@ -255,85 +273,95 @@ pub trait ApiHandler: Send + Sync {
Ok(Some(search_tree))
}

// move to monorepo.
async fn search_and_create_tree(&self, path: &Path) -> Result<Vec<Tree>, GitError> {
/// Searches for a tree in the Git repository by its path, creating intermediate trees if necessary,
/// and returns the trees involved in the update process.
///
/// # Arguments
///
/// * `path` - A reference to the path to search for.
///
/// # Returns
///
/// A vector of trees involved in the update process.
///
/// # Errors
///
/// Returns a `GitError` if an error occurs during the search or tree creation process.
async fn search_and_create_tree(&self, path: &Path) -> Result<VecDeque<Tree>, GitError> {
let relative_path = self.strip_relative(path)?;
let root_tree = self.get_root_tree().await;
let mut search_tree = root_tree.clone();
let mut update_item_tree = VecDeque::new();
update_item_tree.push_back((root_tree, Component::RootDir));
let mut result = vec![];
let mut saving_trees = VecDeque::new();
let mut stack: VecDeque<_> = VecDeque::new();

for component in relative_path.components() {
// skip rootdir
if component != Component::RootDir {
let target_name = component.as_os_str().to_str().unwrap();
let search_res = search_tree
.tree_items
.iter()
.find(|x| x.name == target_name);
if let Some(search_res) = search_res {
let res = self.get_tree_by_hash(&search_res.id.to_plain_str()).await;
search_tree = res.clone();
update_item_tree.push_back((res, component));
} else {
stack.push_back(component)
}
if component == Component::RootDir {
continue;
}

let target_name = component.as_os_str().to_str().unwrap();
if let Some(search_res) = search_tree
.tree_items
.iter()
.find(|x| x.name == target_name)
{
search_tree = self.get_tree_by_hash(&search_res.id.to_plain_str()).await;
update_item_tree.push_back((search_tree.clone(), component));
} else {
stack.push_back(component);
}
}

let blob = converter::generate_git_keep_with_timestamp();
let new_item = TreeItem {
let mut last_tree = Tree::from_tree_items(vec![TreeItem {
mode: TreeItemMode::Blob,
id: blob.id,
name: String::from(".gitkeep"),
};
let mut last_tree = Tree::from_tree_items(vec![new_item]).unwrap();
}])
.unwrap();
let mut last_tree_name = "";
let mut first_element = true;

while let Some(component) = stack.pop_back() {
if first_element {
first_element = false;
} else {
let new_item = TreeItem {
last_tree = Tree::from_tree_items(vec![TreeItem {
mode: TreeItemMode::Tree,
id: last_tree.id,
name: last_tree_name.to_owned(),
};
last_tree = Tree::from_tree_items(vec![new_item]).unwrap();
}])
.unwrap();
}
result.push(last_tree.clone());
saving_trees.push_back(last_tree.clone());
last_tree_name = component.as_os_str().to_str().unwrap();
}

let (new_item_tree, search_name) = update_item_tree.pop_back().unwrap();
let new_item = TreeItem {
mode: TreeItemMode::Tree,
id: last_tree.id,
name: last_tree_name.to_owned(),
};
let mut items = new_item_tree.tree_items.clone();
items.push(new_item);
last_tree = Tree::from_tree_items(items).unwrap();
result.push(last_tree.clone());

let mut replace_hash = last_tree.id;
let mut search_name = search_name.as_os_str().to_str().unwrap();
while let Some((mut tree, component)) = update_item_tree.pop_back() {
let index = tree
.tree_items
.iter()
.position(|x| x.name == search_name)
.unwrap();
tree.tree_items[index].id = replace_hash;
let new_tree = Tree::from_tree_items(tree.tree_items).unwrap();
replace_hash = new_tree.id;
search_name = component.as_os_str().to_str().unwrap();
result.push(new_tree)
if let Some((mut new_item_tree, search_name_component)) = update_item_tree.pop_back() {
new_item_tree.tree_items.push(TreeItem {
mode: TreeItemMode::Tree,
id: last_tree.id,
name: last_tree_name.to_owned(),
});
last_tree = Tree::from_tree_items(new_item_tree.tree_items).unwrap();
saving_trees.push_back(last_tree.clone());

let mut replace_hash = last_tree.id;
let mut search_name = search_name_component.as_os_str().to_str().unwrap();
while let Some((mut tree, component)) = update_item_tree.pop_back() {
if let Some(index) = tree.tree_items.iter().position(|x| x.name == search_name) {
tree.tree_items[index].id = replace_hash;
let new_tree = Tree::from_tree_items(tree.tree_items).unwrap();
replace_hash = new_tree.id;
search_name = component.as_os_str().to_str().unwrap();
saving_trees.push_back(new_tree);
}
}
}
Ok(result)

Ok(saving_trees)
}

async fn reachable_in_tree(
Expand Down
55 changes: 36 additions & 19 deletions ceres/src/api_service/mono_api_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ use mercury::hash::SHA1;
use mercury::internal::object::blob::Blob;
use mercury::internal::object::commit::Commit;
use mercury::internal::object::tree::{Tree, TreeItem, TreeItemMode};
use venus::import_repo::repo::Repo;
use venus::monorepo::converter;

use crate::api_service::ApiHandler;
use crate::model::create_file::CreateFileInfo;
use crate::model::mr::{MRDetail, MrInfoItem};
use crate::model::publish_path::PublishPathInfo;

#[derive(Clone)]
pub struct MonoApiService {
Expand All @@ -27,14 +29,25 @@ pub struct MonoApiService {

#[async_trait]
impl ApiHandler for MonoApiService {
/// Creates a new file or directory in the monorepo based on the provided file information.
///
/// # Arguments
///
/// * `file_info` - Information about the file or directory to create.
///
/// # Returns
///
/// Returns `Ok(())` on success, or a `GitError` on failure.
async fn create_monorepo_file(&self, file_info: CreateFileInfo) -> Result<(), GitError> {
let storage = self.context.services.mega_storage.clone();
let path = PathBuf::from(file_info.path);
let mut save_trees = vec![];

let (update_trees, search_tree) = self.search_tree_for_update(&path).await.unwrap();
// Search for the tree to update and get its tree items
let (update_trees, search_tree) = self.search_tree_for_update(&path).await?;
let mut t_items = search_tree.tree_items;

// Create a new tree item based on whether it's a directory or file
let new_item = if file_info.is_directory {
if t_items
.iter()
Expand All @@ -56,46 +69,43 @@ impl ApiHandler for MonoApiService {
name: file_info.name.clone(),
}
} else {
let blob = Blob::from_content(&file_info.content.unwrap());
let mega_blob: mega_blob::Model = (&blob).into();
let mega_blob: mega_blob::ActiveModel = mega_blob.into();
let raw_blob: raw_blob::Model = blob.clone().into();
let raw_blob: raw_blob::ActiveModel = raw_blob.into();
batch_save_model(storage.get_connection(), vec![mega_blob])
.await
.unwrap();
batch_save_model(storage.get_connection(), vec![raw_blob])
.await
.unwrap();
let content = file_info.content.unwrap();
let blob = Blob::from_content(&content);
let mega_blob: mega_blob::ActiveModel = Into::<mega_blob::Model>::into(&blob).into();
let raw_blob: raw_blob::ActiveModel =
Into::<raw_blob::Model>::into(blob.clone()).into();

let conn = storage.get_connection();
batch_save_model(conn, vec![mega_blob]).await.unwrap();
batch_save_model(conn, vec![raw_blob]).await.unwrap();
TreeItem {
mode: TreeItemMode::Blob,
id: blob.id,
name: file_info.name.clone(),
}
};
// Add the new item to the tree items and create a new tree
t_items.push(new_item);
let p_tree = Tree::from_tree_items(t_items).unwrap();

// Create a commit for the new tree
let refs = storage.get_ref("/").await.unwrap().unwrap();
let commit = Commit::from_tree_id(
p_tree.id,
vec![SHA1::from_str(&refs.ref_commit_hash).unwrap()],
&format!("create file {} commit", file_info.name),
);

let commit_id = self
.update_parent_tree(path, update_trees, commit)
.await
.unwrap();
// Update the parent tree with the new commit
let commit_id = self.update_parent_tree(path, update_trees, commit).await?;
save_trees.push(p_tree);

let save_trees = save_trees
let save_trees: Vec<mega_tree::ActiveModel> = save_trees
.into_iter()
.map(|save_t| {
let mut tree_model: mega_tree::Model = save_t.into();
tree_model.commit_id.clone_from(&commit_id);
let tree_model: mega_tree::ActiveModel = tree_model.into();
tree_model
tree_model.into()
})
.collect();
batch_save_model(storage.get_connection(), save_trees)
Expand All @@ -104,6 +114,13 @@ impl ApiHandler for MonoApiService {
Ok(())
}

async fn publish_path(&self, publish_info: PublishPathInfo) -> Result<(), GitError> {
let storage = self.context.services.git_db_storage.clone();
let repo: Repo = publish_info.into();
storage.save_git_repo(repo.clone()).await.unwrap();
Ok(())
}

async fn get_raw_blob_by_hash(&self, hash: &str) -> Result<Option<raw_blob::Model>, MegaError> {
self.context
.services
Expand Down
1 change: 1 addition & 0 deletions ceres/src/model/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod create_file;
pub mod mr;
pub mod query;
pub mod tree;
pub mod publish_path;

#[derive(PartialEq, Eq, Debug, Clone, Default, Serialize, Deserialize)]

Expand Down
Loading