diff --git a/giterated-daemon/src/backend/git.rs b/giterated-daemon/src/backend/git.rs index 6bf1493..6fa7275 100644 --- a/giterated-daemon/src/backend/git.rs +++ b/giterated-daemon/src/backend/git.rs @@ -497,49 +497,126 @@ impl RepositoryBackend for GitBackend { async fn repository_file_inspect( &mut self, - _requester: Option<&User>, - _request: &RepositoryFileInspectRequest, + requester: Option<&User>, + repository: &Repository, + request: &RepositoryFileInspectRequest, ) -> Result, Error> { - todo!() - } + let repository = match self + .find_by_owner_user_name( + // &request.owner.instance.url, + &repository.owner, + &repository.name, + ) + .await + { + Ok(repository) => repository, + Err(err) => return Err(Box::new(err).into()), + }; - async fn repositories_for_user( - &mut self, - requester: Option<&User>, - user: &User, - ) -> Result, Error> { - let mut repositories = sqlx::query_as!( - GitRepository, - r#"SELECT visibility as "visibility: _", owner_user, name, description, default_branch FROM repositories WHERE owner_user = $1"#, - user.to_string() - ) - .fetch_many(&self.pg_pool); + let git = match repository.open_git2_repository(&self.repository_folder) { + Ok(git) => git, + Err(err) => return Err(Box::new(err).into()), + }; - let mut result = vec![]; + let rev_name = match &request.rev { + None => { + if let Ok(head) = git.head() { + head.name().unwrap().to_string() + } else { + // Nothing in database, render empty tree. + return Ok(vec![]); + } + } + Some(rev_name) => { + // Find the reference, otherwise return GitBackendError + match git + .find_reference(format!("refs/heads/{}", rev_name).as_str()) + .map_err(|_| GitBackendError::RefNotFound(rev_name.to_string())) + { + Ok(reference) => reference.name().unwrap().to_string(), + Err(err) => return Err(Box::new(err).into()), + } + } + }; - while let Some(Ok(Either::Right(repository))) = repositories.next().await { - // Check if the requesting user is allowed to see the repository - if !(matches!( - repository.visibility, - RepositoryVisibility::Unlisted | RepositoryVisibility::Private - ) && Some(&repository.owner_user.clone()) != requester) + // Get the git object as a commit + let rev = match git + .revparse_single(rev_name.as_str()) + .map_err(|_| GitBackendError::RefNotFound(rev_name.to_string())) + { + Ok(rev) => rev, + Err(err) => return Err(Box::new(err).into()), + }; + let commit = rev.as_commit().unwrap(); + + // this is stupid + let mut current_path = rev_name.replace("refs/heads/", ""); + + // Get the commit tree + let git_tree = if let Some(path) = &request.path { + // Add it to our full path string + current_path.push_str(format!("/{}", path).as_str()); + // Get the specified path, return an error if it wasn't found. + let entry = match commit + .tree() + .unwrap() + .get_path(&PathBuf::from(path)) + .map_err(|_| GitBackendError::PathNotFound(path.to_string())) { - result.push(RepositorySummary { - repository: Repository { - owner: repository.owner_user.clone(), - name: repository.name, - instance: self.instance.clone(), - }, - owner: repository.owner_user.clone(), - visibility: repository.visibility, - description: repository.description, - // TODO - last_commit: None, - }); - } - } + Ok(entry) => entry, + Err(err) => return Err(Box::new(err).into()), + }; + // Turn the entry into a git tree + entry.to_object(&git).unwrap().as_tree().unwrap().clone() + } else { + commit.tree().unwrap() + }; - Ok(result) + // Iterate over the git tree and collect it into our own tree types + let mut tree = git_tree + .iter() + .map(|entry| { + let object_type = match entry.kind().unwrap() { + ObjectType::Tree => RepositoryObjectType::Tree, + ObjectType::Blob => RepositoryObjectType::Blob, + _ => unreachable!(), + }; + let mut tree_entry = + RepositoryTreeEntry::new(entry.name().unwrap(), object_type, entry.filemode()); + + if request.extra_metadata { + // Get the file size if It's a blob + let object = entry.to_object(&git).unwrap(); + if let Some(blob) = object.as_blob() { + tree_entry.size = Some(blob.size()); + } + + // Could possibly be done better + let path = if let Some(path) = current_path.split_once('/') { + format!("{}/{}", path.1, entry.name().unwrap()) + } else { + entry.name().unwrap().to_string() + }; + + // Get the last commit made to the entry + if let Ok(last_commit) = + GitBackend::get_last_commit_of_file(&path, &git, commit) + { + tree_entry.last_commit = Some(last_commit); + } + } + + tree_entry + }) + .collect::>(); + + // Sort the tree alphabetically and with tree first + tree.sort_unstable_by_key(|entry| entry.name.to_lowercase()); + tree.sort_unstable_by_key(|entry| { + std::cmp::Reverse(format!("{:?}", entry.object_type).to_lowercase()) + }); + + todo!() } } diff --git a/giterated-daemon/src/backend/mod.rs b/giterated-daemon/src/backend/mod.rs index ff3ee91..2a59ac5 100644 --- a/giterated-daemon/src/backend/mod.rs +++ b/giterated-daemon/src/backend/mod.rs @@ -34,6 +34,7 @@ pub trait RepositoryBackend { async fn repository_file_inspect( &mut self, requester: Option<&User>, + repository: &Repository, request: &RepositoryFileInspectRequest, ) -> Result, Error>; async fn repositories_for_user( diff --git a/giterated-daemon/src/database_backend/handler.rs b/giterated-daemon/src/database_backend/handler.rs index 724fa0c..7493e2c 100644 --- a/giterated-daemon/src/database_backend/handler.rs +++ b/giterated-daemon/src/database_backend/handler.rs @@ -5,10 +5,10 @@ use giterated_models::{ error::{GetValueError, OperationError, UserError}, object::{AnyObject, GiteratedObject}, operation::{AnyOperation, GiteratedOperation}, - repository::{Repository, RepositorySummary}, + repository::{Repository, RepositorySummary, RepositoryInfoRequest, RepositoryView, Description, Visibility, DefaultBranch, LatestCommit, RepositoryFileInspectRequest}, settings::{AnySetting, GetSetting, GetSettingError, SetSetting, SetSettingError}, user::{User, UserRepositoriesRequest}, - value::{AnyValue, GetValue}, + value::{AnyValue, GetValue}, object_backend::ObjectBackend, }; use super::DatabaseBackend; @@ -196,6 +196,38 @@ pub fn user_set_setting( .boxed() } +pub fn repository_info( + object: &Repository, + operation: RepositoryInfoRequest, + state: DatabaseBackend +) -> BoxFuture<'static, Result>> { + let object = object.clone(); + + async move { + let object = state.get_object::(&object.to_string()).await.unwrap(); + + let mut repository_backend = state.repository_backend.lock().await; + let tree = repository_backend.repository_file_inspect(None, object.object(), &RepositoryFileInspectRequest { + path: operation.path.ok_or_else(|| OperationError::Operation(GetValueError::InvalidObject))?, + rev: operation.rev, + }).await?; + + let info = RepositoryView { + name: object.object().name, + owner: object.object().owner, + description: object.get::().await.ok(), + visibility: object.get::().await.map_err(|e| OperationError::Internal(e.to_string()))?, + default_branch: object.get::().await.map_err(|e| OperationError::Internal(e.to_string()))?, + latest_commit: object.get::().await.ok(), + tree_rev: operation.rev, + tree, + }; + + Ok(info) + } + .boxed() +} + pub fn repository_get_value( object: &Repository, operation: GetValue>, diff --git a/giterated-daemon/src/database_backend/mod.rs b/giterated-daemon/src/database_backend/mod.rs index c4e649d..cd6aa57 100644 --- a/giterated-daemon/src/database_backend/mod.rs +++ b/giterated-daemon/src/database_backend/mod.rs @@ -19,7 +19,7 @@ use crate::backend::{RepositoryBackend, UserBackend}; use self::handler::{ repository_get_setting, repository_get_value, repository_set_setting, user_get_repositories, - user_get_setting, user_get_value, user_set_setting, OperationHandlers, + user_get_setting, user_get_value, user_set_setting, OperationHandlers, repository_info, }; #[derive(Clone, Debug)] @@ -119,6 +119,7 @@ impl ObjectBackend for DatabaseBackend { let mut handler = OperationHandlers::default(); handler + .insert(repository_info) .insert(repository_get_value) .insert(repository_get_setting) .insert(repository_set_setting);