Transfer git implementation
This commit is contained in:
		
							parent
							
								
									035f8b2872
								
							
						
					
					
						commit
						b1dd07aed1
					
				
					 11 changed files with 542 additions and 0 deletions
				
			
		
							
								
								
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| /target | ||||
| Cargo.lock | ||||
							
								
								
									
										13
									
								
								Cargo.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								Cargo.toml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,13 @@ | |||
| [package] | ||||
| name = "git-social-server" | ||||
| version = "0.1.0" | ||||
| authors = ["Vladan Popovic <vladanovic@gmail.com>"] | ||||
| edition = "2018" | ||||
| 
 | ||||
| [dependencies] | ||||
| chrono = { version = "*" } | ||||
| 
 | ||||
| [dependencies.git2] | ||||
| version = "*" | ||||
| default-features = false | ||||
| features = [] | ||||
							
								
								
									
										22
									
								
								src/errors.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/errors.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| use git2::Error as GitError; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct SocialError { | ||||
|     pub message: String, | ||||
| } | ||||
| 
 | ||||
| impl From<GitError> for SocialError { | ||||
|     fn from(err: GitError) -> Self { | ||||
|         Self { | ||||
|             message: err.message().to_owned(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<&str> for SocialError { | ||||
|     fn from(err: &str) -> Self { | ||||
|         Self { | ||||
|             message: err.to_owned(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										50
									
								
								src/git/blame.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/git/blame.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,50 @@ | |||
| use crate::errors::SocialError; | ||||
| use crate::git::repo::SocialRepo; | ||||
| use crate::git::{SocialBlame, SocialBlameHunk}; | ||||
| use git2::{Blame, BlameHunk, BlameOptions}; | ||||
| use std::io::{BufRead, BufReader}; | ||||
| 
 | ||||
| fn into_social_blame_hunk(bh: BlameHunk, line: String) -> SocialBlameHunk { | ||||
|     SocialBlameHunk { | ||||
|         signature: bh.final_signature().into(), | ||||
|         commit_id: format!("{}", bh.orig_commit_id()), | ||||
|         line, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn into_social_blame(file: String, blame: Blame<'_>, buffer: BufReader<&[u8]>) -> SocialBlame { | ||||
|     let mut hunks: Vec<SocialBlameHunk> = Vec::new(); | ||||
|     for (i, line) in buffer.lines().enumerate() { | ||||
|         if let (Ok(line), Some(hunk)) = (line, blame.get_line(i + 1)) { | ||||
|             let shunk = into_social_blame_hunk(hunk, line); | ||||
|             hunks.push(shunk); | ||||
|         } | ||||
|     } | ||||
|     SocialBlame { file, hunks } | ||||
| } | ||||
| 
 | ||||
| pub trait GitBlame { | ||||
|     fn blame(&self, from_oid: &str, path: &str) -> Result<SocialBlame, SocialError>; | ||||
| } | ||||
| 
 | ||||
| impl GitBlame for SocialRepo { | ||||
|     fn blame(&self, from_oid: &str, path: &str) -> Result<SocialBlame, SocialError> { | ||||
|         let revspec = self.repo.revparse_single(from_oid)?; | ||||
|         let commit_id = revspec.id(); | ||||
| 
 | ||||
|         let spec = format!("{}:{}", commit_id, path); | ||||
|         let object = self.repo.revparse_single(&spec[..])?; | ||||
|         let blob = self.repo.find_blob(object.id())?; | ||||
|         let reader = BufReader::new(blob.content()); | ||||
| 
 | ||||
|         let mut opts = BlameOptions::new(); | ||||
|         opts.track_copies_any_commit_copies(true) | ||||
|             .track_copies_same_commit_copies(true) | ||||
|             .newest_commit(revspec.id()); | ||||
| 
 | ||||
|         self.repo | ||||
|             .blame_file(std::path::Path::new(path), Some(&mut opts)) | ||||
|             .map(|blame| into_social_blame(path.to_owned(), blame, reader)) | ||||
|             .map_err(|d| d.into()) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										47
									
								
								src/git/diff.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/git/diff.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,47 @@ | |||
| use crate::errors::SocialError; | ||||
| use crate::git::repo::SocialRepo; | ||||
| use crate::git::SocialDiff; | ||||
| use git2::{Diff, DiffOptions}; | ||||
| use git2::{DiffFormat, DiffLine}; | ||||
| 
 | ||||
| fn into_social_diff(d: Diff<'_>) -> SocialDiff { | ||||
|     let mut lines: Vec<String> = Vec::new(); | ||||
|     d.print(DiffFormat::Patch, |_, _, line: DiffLine| { | ||||
|         if let Ok(l) = String::from_utf8(line.content().to_vec()) { | ||||
|             lines.push(format!("{} {}", line.origin(), l)); | ||||
|         } else { | ||||
|             lines.push("? cannot convert utf8 string on this line!".to_string()); | ||||
|         } | ||||
|         true | ||||
|     }) | ||||
|     .unwrap_or(()); | ||||
|     SocialDiff { lines } | ||||
| } | ||||
| 
 | ||||
| pub trait Show { | ||||
|     fn show(&self, from_oid: &str, to_oid: &str) -> Result<SocialDiff, SocialError>; | ||||
| } | ||||
| 
 | ||||
| impl Show for SocialRepo { | ||||
|     fn show(&self, from_oid: &str, to_oid: &str) -> Result<SocialDiff, SocialError> { | ||||
|         let mut opts = DiffOptions::new(); | ||||
|         opts.reverse(false) | ||||
|             .force_text(false) | ||||
|             .ignore_whitespace_eol(false) | ||||
|             .ignore_whitespace_change(false) | ||||
|             .ignore_whitespace(false) | ||||
|             .include_ignored(false) | ||||
|             .include_untracked(false) | ||||
|             .patience(true) | ||||
|             .context_lines(5) | ||||
|             .minimal(false); | ||||
| 
 | ||||
|         let t1 = self.get_tree(from_oid).ok(); | ||||
|         let t2 = self.get_tree(to_oid).ok(); | ||||
| 
 | ||||
|         self.repo | ||||
|             .diff_tree_to_tree(t1.as_ref(), t2.as_ref(), Some(&mut opts)) | ||||
|             .map(into_social_diff) | ||||
|             .map_err(|d| d.into()) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										75
									
								
								src/git/log.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/git/log.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,75 @@ | |||
| use crate::errors::SocialError; | ||||
| use crate::git::repo::SocialRepo; | ||||
| use crate::git::SocialCommit; | ||||
| 
 | ||||
| pub trait Log { | ||||
|     fn log<'a>( | ||||
|         &'a self, | ||||
|         start: Option<String>, | ||||
|         end: Option<String>, | ||||
|         count: usize, | ||||
|         short: bool, | ||||
|     ) -> Result<Box<dyn Iterator<Item = SocialCommit> + 'a>, SocialError>; | ||||
| 
 | ||||
|     fn commit(&self, rev: &str) -> Result<SocialCommit, SocialError>; | ||||
|     fn first_commit(&self) -> Result<SocialCommit, SocialError>; | ||||
| } | ||||
| 
 | ||||
| impl Log for SocialRepo { | ||||
|     fn log<'a>( | ||||
|         &'a self, | ||||
|         start: Option<String>, | ||||
|         end: Option<String>, | ||||
|         count: usize, | ||||
|         short: bool, | ||||
|     ) -> Result<Box<dyn Iterator<Item = SocialCommit> + 'a>, SocialError> { | ||||
|         let mut git_revwalk = self.repo.revwalk()?; | ||||
|         let start = start.or_else(|| Some("HEAD".to_owned())); | ||||
| 
 | ||||
|         if let (Some(s), Some(e)) = (&start, &end) { | ||||
|             git_revwalk.push_range(&format!("{}..{}", s, e))?; | ||||
|         } else if let Some(s) = start { | ||||
|             git_revwalk | ||||
|                 .push_ref(&s) | ||||
|                 .or(git_revwalk.push(self.repo.revparse(&s)?.from().unwrap().id()))?; | ||||
|         } else { | ||||
|             git_revwalk.push_head()?; | ||||
|         } | ||||
| 
 | ||||
|         Ok(Box::new( | ||||
|             git_revwalk | ||||
|                 .filter_map(move |idres| idres.map(|id| self.repo.find_commit(id.clone())).ok()) | ||||
|                 .flatten() | ||||
|                 .map(|c| c.into()) | ||||
|                 .map(move |c: SocialCommit| if short { c.oneline() } else { c }) | ||||
|                 .take(count), | ||||
|         )) | ||||
|     } | ||||
| 
 | ||||
|     fn commit(&self, rev: &str) -> Result<SocialCommit, SocialError> { | ||||
|         self.repo | ||||
|             .revparse_single(rev.as_ref()) | ||||
|             .and_then(|o| self.repo.find_commit(o.id())) | ||||
|             .map(|c| c.into()) | ||||
|             .map_err(|e| e.into()) | ||||
|     } | ||||
| 
 | ||||
|     fn first_commit(&self) -> Result<SocialCommit, SocialError> { | ||||
|         let mut revwalk = self.repo.revwalk()?; | ||||
|         revwalk.set_sorting(git2::Sort::REVERSE | git2::Sort::TOPOLOGICAL)?; | ||||
|         revwalk.push_head()?; | ||||
|         revwalk | ||||
|             .filter_map(move |idres| idres.map(|id| self.repo.find_commit(id.clone())).ok()) | ||||
|             .take(1) | ||||
|             .next() | ||||
|             .map(|r| { | ||||
|                 r.map_err(|_| SocialError { | ||||
|                     message: "Invalid commit reference!".to_string(), | ||||
|                 }) | ||||
|                 .map(|c| c.into()) | ||||
|             }) | ||||
|             .ok_or(SocialError { | ||||
|                 message: "Repo doesn't contain any commits!".to_owned(), | ||||
|             })? | ||||
|     } | ||||
| } | ||||
							
								
								
									
										146
									
								
								src/git/mod.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								src/git/mod.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,146 @@ | |||
| pub(crate) mod blame; | ||||
| pub(crate) mod diff; | ||||
| pub(crate) mod log; | ||||
| pub(crate) mod references; | ||||
| pub(crate) mod repo; | ||||
| pub(crate) mod tree; | ||||
| 
 | ||||
| use chrono::offset::{Local, TimeZone}; | ||||
| use chrono::DateTime; | ||||
| use git2::{Commit, Signature}; | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub struct SocialSignature { | ||||
|     pub when: DateTime<Local>, | ||||
|     pub name: String, | ||||
|     pub email: String, | ||||
| } | ||||
| 
 | ||||
| impl Default for SocialSignature { | ||||
|     fn default() -> Self { | ||||
|         Self { | ||||
|             when: Local.timestamp(0, 0), | ||||
|             name: "".to_string(), | ||||
|             email: "".to_string(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub struct SocialCommit { | ||||
|     pub id: String, | ||||
|     pub time: DateTime<Local>, | ||||
|     pub message: String, | ||||
|     pub author: SocialSignature, | ||||
|     pub committer: SocialSignature, | ||||
|     pub diff: Option<SocialDiff>, | ||||
|     pub parent_ids: Option<Vec<String>>, // TODO: Use iterator instead of vec.
 | ||||
| } | ||||
| 
 | ||||
| impl SocialCommit { | ||||
|     pub fn oneline(self) -> Self { | ||||
|         Self { | ||||
|             message: self.message.lines().next().unwrap_or("").to_owned(), | ||||
|             parent_ids: None, | ||||
|             diff: None, | ||||
|             ..self | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Default for SocialCommit { | ||||
|     fn default() -> Self { | ||||
|         Self { | ||||
|             id: "".to_string(), | ||||
|             time: Local.timestamp(0, 0), | ||||
|             message: "".to_string(), | ||||
|             author: SocialSignature::default(), | ||||
|             committer: SocialSignature::default(), | ||||
|             diff: None, | ||||
|             parent_ids: None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<Signature<'_>> for SocialSignature { | ||||
|     fn from(s: Signature<'_>) -> Self { | ||||
|         Self { | ||||
|             name: s.name().unwrap_or("").to_owned(), | ||||
|             email: s.email().unwrap_or("").to_owned(), | ||||
|             when: Local.timestamp(s.when().seconds(), 0), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<Commit<'_>> for SocialCommit { | ||||
|     fn from(c: Commit<'_>) -> Self { | ||||
|         Self { | ||||
|             id: format!("{}", c.id()), | ||||
|             time: Local.timestamp(c.time().seconds(), 0), | ||||
|             message: c.message().unwrap_or("").to_owned(), | ||||
|             author: c.author().into(), | ||||
|             committer: c.committer().into(), | ||||
|             diff: None, | ||||
|             parent_ids: None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, Default)] | ||||
| pub struct SocialDiff { | ||||
|     pub lines: Vec<String>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, Default)] | ||||
| pub struct SocialTreeEntry { | ||||
|     pub id: String, | ||||
|     pub name: String, | ||||
|     pub kind: String, | ||||
|     pub filemode: i32, | ||||
|     pub size: usize, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, Default)] | ||||
| pub struct SocialFileContent { | ||||
|     pub path: String, | ||||
|     pub content: String, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, Default)] | ||||
| pub struct SocialBranch { | ||||
|     pub name: String, | ||||
|     pub head: SocialCommit, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, Default)] | ||||
| pub struct SocialTag { | ||||
|     pub name: String, | ||||
|     pub head: SocialCommit, | ||||
|     pub annotation: Option<String>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, Default)] | ||||
| pub struct SocialRefs { | ||||
|     pub branches: Vec<SocialBranch>, | ||||
|     pub tags: Vec<SocialTag>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, Default)] | ||||
| pub struct SocialBlameHunk { | ||||
|     pub commit_id: String, | ||||
|     pub line: String, | ||||
|     pub signature: SocialSignature, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, Default)] | ||||
| pub struct SocialBlame { | ||||
|     pub file: String, | ||||
|     pub hunks: Vec<SocialBlameHunk>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, Default)] | ||||
| pub struct SocialRepoInfo { | ||||
|     pub name: String, | ||||
|     pub author: SocialSignature, | ||||
|     pub description: String, | ||||
| } | ||||
							
								
								
									
										66
									
								
								src/git/references.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/git/references.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,66 @@ | |||
| use crate::errors::SocialError; | ||||
| use crate::git::repo::SocialRepo; | ||||
| use crate::git::{SocialBranch, SocialCommit, SocialTag}; | ||||
| use git2::{Branch, BranchType, Commit}; | ||||
| 
 | ||||
| fn into_social_branch(b: Branch<'_>) -> SocialBranch { | ||||
|     SocialBranch { | ||||
|         name: b.name().unwrap_or(Some("")).unwrap_or("").to_owned(), | ||||
|         head: SocialCommit::default(), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn into_social_tag(name: Option<&str>) -> SocialTag { | ||||
|     SocialTag { | ||||
|         name: name.unwrap_or("").to_owned(), | ||||
|         head: SocialCommit::default(), | ||||
|         annotation: None, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait Refs { | ||||
|     fn branches(&self) -> Result<Vec<SocialBranch>, SocialError>; | ||||
|     fn tags(&self) -> Result<Vec<SocialTag>, SocialError>; | ||||
| } | ||||
| 
 | ||||
| impl Refs for SocialRepo { | ||||
|     fn branches(&self) -> Result<Vec<SocialBranch>, SocialError> { | ||||
|         let all_branches = self.repo.branches(Some(BranchType::Local))?; | ||||
|         let mut resulting_brances: Vec<SocialBranch> = vec![]; | ||||
| 
 | ||||
|         for r in all_branches { | ||||
|             r.map(|(branch, _)| { | ||||
|                 let mut sb = into_social_branch(branch); | ||||
|                 self.repo | ||||
|                     .revparse_single(&sb.name) | ||||
|                     .map(|obj| { | ||||
|                         obj.peel_to_commit() | ||||
|                             .map(|c: Commit| { | ||||
|                                 sb.head = c.into(); | ||||
|                                 resulting_brances.push(sb); | ||||
|                             }) | ||||
|                             .unwrap_or(()); | ||||
|                     }) | ||||
|                     .unwrap_or(()); | ||||
|             }) | ||||
|             .unwrap_or(()); | ||||
|         } | ||||
|         Ok(resulting_brances) | ||||
|     } | ||||
| 
 | ||||
|     fn tags(&self) -> Result<Vec<SocialTag>, SocialError> { | ||||
|         let tagnames = self.repo.tag_names(None)?; | ||||
|         let mut result: Vec<SocialTag> = vec![]; | ||||
| 
 | ||||
|         for t in tagnames.into_iter() { | ||||
|             let mut st = into_social_tag(t); | ||||
|             let _ = self.repo.revparse_single(&st.name).map(|obj| { | ||||
|                 obj.peel_to_commit().map(|c: Commit| { | ||||
|                     st.head = c.into(); | ||||
|                     result.push(st); | ||||
|                 }) | ||||
|             }); | ||||
|         } | ||||
|         Ok(result) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										31
									
								
								src/git/repo.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/git/repo.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | |||
| use crate::errors::SocialError; | ||||
| use git2::{Error as GitError, Repository, Tree}; | ||||
| use std::{fs::read_to_string, path::Path}; | ||||
| 
 | ||||
| pub struct SocialRepo { | ||||
|     pub repo: Repository, | ||||
| } | ||||
| 
 | ||||
| impl<'repo> SocialRepo { | ||||
|     pub fn new(path: &str) -> Result<Self, SocialError> { | ||||
|         Repository::open(path) | ||||
|             .map(|repo| Self { repo }) | ||||
|             .map_err(|_| { | ||||
|                 let err = "fatal: not a git repository"; | ||||
|                 SocialError::from(err) | ||||
|             }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_tree(&self, rev: &str) -> Result<Tree, GitError> { | ||||
|         self.repo | ||||
|             .revparse_single(rev) | ||||
|             .and_then(|obj| obj.peel_to_tree()) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_description(&self) -> String { | ||||
|         // The description is in a file named "description",
 | ||||
|         // in the .git directory, or the root directory if bare.
 | ||||
|         let git_path = self.repo.path().join(Path::new("description")); | ||||
|         read_to_string(git_path).unwrap_or("".to_string()) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										82
									
								
								src/git/tree.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/git/tree.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,82 @@ | |||
| use crate::errors::SocialError; | ||||
| use crate::git::repo::SocialRepo; | ||||
| use crate::git::{SocialFileContent, SocialTreeEntry}; | ||||
| use git2::{Blob, Error as GitError, Object, ObjectType, TreeEntry}; | ||||
| use std::path::Path; | ||||
| 
 | ||||
| fn into_social_tree_entry(te: TreeEntry<'_>, size: usize) -> SocialTreeEntry { | ||||
|     SocialTreeEntry { | ||||
|         id: format!("{}", te.id()), | ||||
|         name: te.name().unwrap_or("").to_owned(), | ||||
|         kind: te.kind().map(|ot| ot.str()).unwrap_or("any").to_owned(), | ||||
|         filemode: te.filemode(), | ||||
|         size, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait LsTree { | ||||
|     fn ls_tree(&self, rev: &str, path: &Path) -> Result<Vec<SocialTreeEntry>, SocialError>; | ||||
|     fn show_file(&self, rev: &str, path: &Path) -> Result<SocialFileContent, SocialError>; | ||||
| } | ||||
| 
 | ||||
| impl LsTree for SocialRepo { | ||||
|     fn ls_tree(&self, rev: &str, path: &Path) -> Result<Vec<SocialTreeEntry>, SocialError> { | ||||
|         let matched_tree = if path.to_str().unwrap_or("") == "" { | ||||
|             self.get_tree(rev)? | ||||
|         } else { | ||||
|             self.get_tree(rev) | ||||
|                 .and_then(|t| t.get_path(&path)) | ||||
|                 .and_then(|entry: TreeEntry| self.repo.find_tree(entry.id()))? | ||||
|         }; | ||||
| 
 | ||||
|         let mut result: Vec<SocialTreeEntry> = vec![]; | ||||
|         let mut files: Vec<SocialTreeEntry> = vec![]; | ||||
|         for entry in matched_tree.iter() { | ||||
|             match entry.kind() { | ||||
|                 Some(git2::ObjectType::Blob) => { | ||||
|                     self.repo | ||||
|                         .revparse_single(&format!("{}", entry.id())) | ||||
|                         .map(|obj: Object| { | ||||
|                             obj.as_blob().map(|b: &Blob| { | ||||
|                                 files.push(into_social_tree_entry(entry, b.content().len())) | ||||
|                             }); | ||||
|                         }) | ||||
|                         .unwrap_or(()); | ||||
|                 } | ||||
|                 Some(git2::ObjectType::Tree) => result.push(into_social_tree_entry(entry, 0)), | ||||
|                 _ => (), | ||||
|             } | ||||
|         } | ||||
|         result.extend(files); | ||||
|         Ok(result) | ||||
|     } | ||||
| 
 | ||||
|     fn show_file(&self, rev: &str, path: &Path) -> Result<SocialFileContent, SocialError> { | ||||
|         self.get_tree(rev) | ||||
|             .and_then(|t| t.get_path(&path)) | ||||
|             .and_then(|entry: TreeEntry| match entry.kind() { | ||||
|                 Some(ObjectType::Blob) => self | ||||
|                     .repo | ||||
|                     .revparse_single(&format!("{}", entry.id())) | ||||
|                     .and_then(|obj: Object| { | ||||
|                         obj.as_blob() | ||||
|                             .ok_or_else(|| { | ||||
|                                 GitError::from_str(&format!("Cannot get blob for {}", obj.id())) | ||||
|                             }) | ||||
|                             .and_then(|blob: &Blob| { | ||||
|                                 String::from_utf8(blob.content().to_vec()).map_err(|_| { | ||||
|                                     GitError::from_str( | ||||
|                                         format!("Cannot read file {}", path.display()).as_ref(), | ||||
|                                     ) | ||||
|                                 }) | ||||
|                             }) | ||||
|                             .map(|content| SocialFileContent { | ||||
|                                 content, | ||||
|                                 path: format!("{}", path.display()), | ||||
|                             }) | ||||
|                     }), | ||||
|                 _ => Result::Err(GitError::from_str("Object is not a Blob!")), | ||||
|             }) | ||||
|             .map_err(|e| e.into()) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										8
									
								
								src/main.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/main.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | |||
| mod errors; | ||||
| mod git; | ||||
| 
 | ||||
| use git::repo::SocialRepo; | ||||
| 
 | ||||
| fn main() { | ||||
|     SocialRepo::new("/home/vladan/dev/git.social/git-social-server").ok(); | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue