display blobs
This commit is contained in:
parent
27102f1a58
commit
dd446e25a1
5 changed files with 184 additions and 39 deletions
|
@ -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"
|
||||
|
|
203
src/main.rs
203
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<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
10
templates/repo_blob.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
<html>
|
||||
<head>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>{{ repo_name }}: {{ tree_name }}: {{ blob.name }}</h1>
|
||||
|
||||
<pre>{{ blob.contents }}</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue