display blobs

This commit is contained in:
Michael Zhang 2020-04-21 09:33:34 -05:00
parent 27102f1a58
commit dd446e25a1
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
5 changed files with 184 additions and 39 deletions

View file

@ -4,6 +4,8 @@ version = "0.1.0"
authors = ["Michael Zhang <iptq@protonmail.com>"]
edition = "2018"
[workspace]
[dependencies]
git2 = { version = "0.13.0", default-features = false }
serde = "1.0.105"

View file

@ -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<Vec<PathBuf>> {
pub fn get_dir_list(&self) -> Result<Vec<PathBuf>> {
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<Response<Body>> {
let directories = self.get_dir_list().await?;
pub fn render_index(&self) -> Result<Response<Body>> {
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<Response<Body>> {
pub fn render_repo_index(&self, path: PathBuf, repo: &Repository) -> Result<Response<Body>> {
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<PathBuf>,
) -> Result<Response<Body>> {
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<Body>) -> Result<Response<Body>> {
pub fn render_repo_blob(
&self,
path: PathBuf,
repo: &Repository,
tree_name: String,
filepath: impl AsRef<Path>,
blob_id: Oid,
) -> Result<Response<Body>> {
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<Body>) -> Result<Response<Body>> {
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::<PathBuf>();
println!("filepath: {:?}", filepath);
if filepath.iter().collect::<Vec<_>>().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<str>) -> 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<Path>,
) -> 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::<PathBuf>())
}
_ => 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))))
}
})
};

10
templates/repo_blob.html Normal file
View file

@ -0,0 +1,10 @@
<html>
<head>
</head>
<body>
<h1>{{ repo_name }}: {{ tree_name }}: {{ blob.name }}</h1>
<pre>{{ blob.contents }}</pre>
</body>
</html>

View file

@ -3,12 +3,12 @@
</head>
<body>
<h1>{{ name }}</h1>
<h1>{{ repo_name }}</h1>
<h3>branches:</h3>
<ul>
{% for branch in branches %}
{{ branch }}
<a href="{{ branch.url }}">{{ branch.name }}</a>
{% endfor %}
</ul>
</body>

View file

@ -3,12 +3,12 @@
</head>
<body>
<h1>{{ name }}: {{ ref }}</h1>
<h1>{{ repo_name }}: {{ tree_name }}{% if filepath %}: {{ filepath }}{% endif %}</h1>
<h3>entries:</h3>
<ul>
{% for entry in entries %}
<li>{{ entry.name }}</li>
<li><a href="{{ entry.url }}">{{ entry.name }}</a></li>
{% endfor %}
</ul>
</body>