From dd446e25a1fe12c2db71a34f87bd90e6516307a6 Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Tue, 21 Apr 2020 09:33:34 -0500 Subject: [PATCH] display blobs --- Cargo.toml | 2 + src/main.rs | 203 +++++++++++++++++++++++++++++++------- templates/repo_blob.html | 10 ++ templates/repo_index.html | 4 +- templates/repo_tree.html | 4 +- 5 files changed, 184 insertions(+), 39 deletions(-) create mode 100644 templates/repo_blob.html diff --git a/Cargo.toml b/Cargo.toml index b987440..d127f35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" authors = ["Michael Zhang "] edition = "2018" +[workspace] + [dependencies] git2 = { version = "0.13.0", default-features = false } serde = "1.0.105" diff --git a/src/main.rs b/src/main.rs index 73053b0..9c2e9f9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,14 +8,14 @@ extern crate serde_json; mod config; use std::convert::Infallible; -use std::net::SocketAddr; -use std::path::{Component as PathComponent, PathBuf}; + +use std::path::{Component as PathComponent, Path, PathBuf}; use std::sync::Arc; use anyhow::Result; -use futures::future::{self, TryFutureExt}; -use git2::Repository; +use futures::future::{self}; +use git2::{ObjectType, Oid, Repository, Tree}; use hyper::{ service::{make_service_fn, service_fn}, Body, Request, Response, Server, StatusCode, @@ -40,6 +40,7 @@ lazy_static! { let mut tera = Tera::default(); tera.add_raw_templates(vec![ template!("index.html"), + template!("repo_blob.html"), template!("repo_index.html"), template!("repo_tree.html"), ]) @@ -68,7 +69,7 @@ impl Fedhub { }) } - pub async fn get_dir_list(&self) -> Result> { + pub fn get_dir_list(&self) -> Result> { let state = self.state.read(); let mut con = state.cache.get_connection()?; if RedisCmd::exists("repos").query(&mut con)? { @@ -102,63 +103,114 @@ impl Fedhub { } } - pub async fn render_index(&self) -> Result> { - let directories = self.get_dir_list().await?; + pub fn render_index(&self) -> Result> { + let directories = self.get_dir_list()?; let mut ctx = TeraContext::new(); ctx.insert("repos", &directories); Ok(Response::new(TERA.render("index.html", &ctx)?.into())) } - pub async fn render_repo_index( - &self, - path: PathBuf, - repo: Repository, - ) -> Result> { + pub fn render_repo_index(&self, path: PathBuf, repo: &Repository) -> Result> { let mut ctx = TeraContext::new(); - ctx.insert("name", &path); + ctx.insert("repo_name", &path); let mut branches = Vec::new(); for branch in repo.branches(None)? { let (branch, _) = branch?; - branches.push(branch.name()?.unwrap().to_string()); + let name = branch.name()?.unwrap().to_string(); + let branch_ref = branch.into_reference(); + let url = format!( + "/{}/+/{}", + path.to_str().unwrap(), + branch_ref.name().unwrap() + ); + branches.push(json!({ + "name": name, + "url": url, + })); } ctx.insert("branches", &branches); return Ok(Response::new(TERA.render("repo_index.html", &ctx)?.into())); } - pub async fn render_repo_tree( + pub fn render_repo_tree( &self, path: PathBuf, - repo: Repository, - tree: String, + repo: &Repository, + tree_id: Oid, + tree_name: String, + filepath: Option, ) -> Result> { - let commit_ref = repo.resolve_reference_from_short_name(tree.as_ref())?; - let commit = commit_ref.peel_to_commit()?; - let tree = commit.tree()?; + let tree = repo.find_tree(tree_id)?; let mut ctx = TeraContext::new(); - ctx.insert("name", &path); - ctx.insert("ref", commit_ref.shorthand().unwrap()); + ctx.insert("repo_name", &path); + ctx.insert("tree_name", &tree_name); + ctx.insert("filepath", &filepath); let mut entries = Vec::new(); for entry in tree.iter() { + let url = match &filepath { + Some(filepath) => format!( + "/{}/+/{}/{}/{}", + path.to_str().unwrap(), + tree_name, + filepath.to_str().unwrap(), + entry.name().unwrap() + ), + None => format!( + "/{}/+/{}/{}", + path.to_str().unwrap(), + tree_name, + entry.name().unwrap() + ), + }; entries.push(json!({ "name": entry.name().unwrap(), + "url": url, })); } ctx.insert("entries", &entries); return Ok(Response::new(TERA.render("repo_tree.html", &ctx)?.into())); } - pub async fn handle(self, req: Request) -> Result> { + pub fn render_repo_blob( + &self, + path: PathBuf, + repo: &Repository, + tree_name: String, + filepath: impl AsRef, + blob_id: Oid, + ) -> Result> { + let filepath = filepath.as_ref(); + let blob = repo.find_blob(blob_id)?; + let mut ctx = TeraContext::new(); + ctx.insert("repo_name", &path); + ctx.insert("tree_name", &tree_name); + ctx.insert( + "blob", + &json!({ + "name": filepath, + "binary": blob.is_binary(), + "contents": if blob.is_binary() { + json!(null) + } else { + json!(String::from_utf8(blob.content().to_vec())?) + }, + }), + ); + return Ok(Response::new(TERA.render("repo_blob.html", &ctx)?.into())); + } + + pub fn handle(self, req: Request) -> Result> { let repo_root = self.config.repo_root.clone(); let uri = req.uri(); let path = uri.path(); if path == "/" { - return self.render_index().await; + return self.render_index(); } let repo_info = { let mut repo_info = None; - for repo_dir in self.get_dir_list().await? { + for repo_dir in self.get_dir_list()? { let path = PathBuf::from(path.trim_start_matches("/")); if path.starts_with(&repo_dir) { let repo = Repository::open(repo_root.join(&repo_dir))?; @@ -171,13 +223,53 @@ impl Fedhub { if let Some((path, repo, remainder)) = repo_info { if remainder == PathBuf::from("/") { - return self.render_repo_index(path, repo).await; - } else if remainder.starts_with("/tree") { - let tree = remainder.strip_prefix("/tree").unwrap().components().next(); - if let Some(PathComponent::Normal(tree)) = tree { - return self - .render_repo_tree(path, repo, tree.to_str().unwrap().to_string()) - .await; + return self.render_repo_index(path, &repo); + } else if remainder.starts_with("/+") { + let mut iter = remainder.strip_prefix("/+").unwrap().components(); + let tree_name = iter.next(); + if let Some(PathComponent::Normal(tree_name)) = tree_name { + let tree_name = tree_name.to_str().unwrap().to_string(); + let tree_name = if tree_name == "refs" { + let second = iter.next().unwrap(); + let third = iter.next().unwrap(); + format!( + "{}/{}/{}", + tree_name, + second.as_os_str().to_str().unwrap(), + third.as_os_str().to_str().unwrap() + ) + } else { + tree_name + }; + let (tree_id, shortname) = { + let (tree, shortname) = get_tree_from_name(&repo, tree_name)?; + (tree.id(), shortname) + }; + let filepath = iter.collect::(); + println!("filepath: {:?}", filepath); + if filepath.iter().collect::>().len() == 0 { + return self.render_repo_tree(path, &repo, tree_id, shortname, None); + } else { + let (object_id, object_type) = + get_path_from_tree(&repo, tree_id, &filepath)?; + println!("{:?} {:?}", object_id, object_type); + match object_type { + ObjectType::Tree => { + return self.render_repo_tree( + path, + &repo, + object_id, + shortname, + Some(filepath), + ) + } + ObjectType::Blob => { + return self + .render_repo_blob(path, &repo, shortname, filepath, object_id) + } + _ => unimplemented!(), + } + } } } } @@ -189,6 +281,44 @@ impl Fedhub { } } +fn get_tree_from_name(repo: &Repository, name: impl AsRef) -> Result<(Tree, String)> { + Ok( + if let Ok(commit_ref) = repo.resolve_reference_from_short_name(name.as_ref()) { + let tree_name = commit_ref.shorthand().unwrap().to_string(); + (commit_ref.peel_to_commit()?.tree()?, tree_name) + } else if let Ok(commit) = repo.find_commit(Oid::from_str(name.as_ref())?) { + let tree_name = name.as_ref().to_string(); + (commit.tree()?, tree_name) + } else { + anyhow::bail!("Fuck!"); + }, + ) +} + +fn get_path_from_tree<'a>( + repo: &Repository, + curr_oid: Oid, + path: impl AsRef, +) -> Result<(Oid, ObjectType)> { + let mut components = path.as_ref().components(); + let tree = repo.find_tree(curr_oid)?; + if let Some(next_file) = components.next() { + if let Some(entry) = tree.get_name(next_file.as_os_str().to_str().unwrap()) { + let kind = entry.kind().unwrap(); + match kind { + ObjectType::Tree => { + get_path_from_tree(repo, entry.id(), components.collect::()) + } + _ => Ok((entry.id(), kind)), + } + } else { + anyhow::bail!("doesn't exist bro: {:?}", next_file); + } + } else { + Ok((tree.id(), ObjectType::Tree)) + } +} + #[tokio::main] async fn main() -> Result<()> { let mut config_file = File::open("config.toml").await?; @@ -204,9 +334,12 @@ async fn main() -> Result<()> { let fedhub = fedhub.clone(); let main = move |req| { let fedhub = fedhub.clone(); - fedhub.handle(req).or_else(|err| { - eprintln!("Error: {:?}", err); - future::ok::<_, Infallible>(Response::new(Body::from(format!("Error: {:?}", err)))) + future::ready(match fedhub.handle(req) { + Ok(res) => Ok::<_, Infallible>(res), + Err(err) => { + eprintln!("Error: {:?}", err); + Ok(Response::new(Body::from(format!("Error: {:?}", err)))) + } }) }; diff --git a/templates/repo_blob.html b/templates/repo_blob.html new file mode 100644 index 0000000..fbca800 --- /dev/null +++ b/templates/repo_blob.html @@ -0,0 +1,10 @@ + + + + + +

{{ repo_name }}: {{ tree_name }}: {{ blob.name }}

+ +
{{ blob.contents }}
+ + diff --git a/templates/repo_index.html b/templates/repo_index.html index 5b1693c..e441618 100644 --- a/templates/repo_index.html +++ b/templates/repo_index.html @@ -3,12 +3,12 @@ -

{{ name }}

+

{{ repo_name }}

branches:

diff --git a/templates/repo_tree.html b/templates/repo_tree.html index 957a182..864ca95 100644 --- a/templates/repo_tree.html +++ b/templates/repo_tree.html @@ -3,12 +3,12 @@ -

{{ name }}: {{ ref }}

+

{{ repo_name }}: {{ tree_name }}{% if filepath %}: {{ filepath }}{% endif %}

entries: