use actix_web::{
http::header::{ContentDisposition, TryIntoHeaderValue as _, CONTENT_DISPOSITION},
HttpResponse,
};
use anyhow::{anyhow, Result};
use askama::Template;
use git2::Repository;
use std::str;
pub struct Breadcrumb {
pub name: String,
pub link: String,
}
#[derive(Template)]
#[template(path = "file.html")]
struct FileView<'a, 'b> {
file_content: String,
file_path: &'a str,
repo_name: &'b str,
breadcrumbs: Vec<Breadcrumb>,
}
pub fn get_file_breadcrumbs(repo_owner: &str, repo_name: &str, file_path: &str) -> Vec<Breadcrumb> {
let mut breadcrumbs = vec![
Breadcrumb {
name: String::from(".."),
link: format!("/"),
},
Breadcrumb {
name: format!("~{}", repo_owner),
link: format!("/~{}", repo_owner),
},
Breadcrumb {
name: repo_name.to_string(),
link: format!("/~{}/{}", repo_owner, repo_name),
},
];
let mut breadcrumb_link = format!("/~{}/{}/tree", repo_owner, repo_name);
for segment in file_path.split("/") {
breadcrumb_link += &format!("/{}", segment);
breadcrumbs.push(Breadcrumb {
name: segment.to_string(),
link: breadcrumb_link.clone(),
})
}
breadcrumbs
}
pub fn render_binary_object(blob: &git2::Blob, path: &str) -> HttpResponse {
let path = std::path::Path::new(path);
let file_name = path
.file_name()
.unwrap_or_default()
.to_str()
.unwrap_or_default();
let extension = path
.extension()
.unwrap_or_default()
.to_str()
.unwrap_or_default()
.to_lowercase();
match extension.as_str() {
"pdf" => HttpResponse::Ok()
.content_type("application/pdf")
.body(blob.content().to_owned()),
"jpg" | "jpeg" => HttpResponse::Ok()
.content_type("image/jpeg")
.body(blob.content().to_owned()),
"png" => HttpResponse::Ok()
.content_type("image/png")
.body(blob.content().to_owned()),
_ => {
let cd = ContentDisposition::attachment(file_name);
let mut resp = HttpResponse::Ok().body(blob.content().to_owned());
resp.headers_mut()
.insert(CONTENT_DISPOSITION, cd.try_into_value().unwrap());
return resp;
}
}
}
pub fn render_file(
repo: &Repository,
repo_owner: &str,
repo_name: &str,
file_path: &str,
) -> Result<String> {
let obj = repo.revparse_single(&format!("HEAD:{}", file_path))?;
let kind = obj.kind();
if kind != Some(git2::ObjectType::Blob) && kind != Some(git2::ObjectType::Tree) {
return Err(anyhow!("{} is not a file or folder", file_path));
}
if kind == Some(git2::ObjectType::Tree) {
return Ok(format!("{} is a folder", file_path));
}
if let Some(blob) = obj.as_blob() {
if let Ok(blob_str) = str::from_utf8(blob.content()) {
let file_view = FileView {
file_content: blob_str.to_string(),
file_path,
repo_name,
breadcrumbs: get_file_breadcrumbs(repo_owner, repo_name, file_path),
};
if let Ok(x) = file_view.render() {
return Ok(x);
} else {
return Err(anyhow!("failed to render"));
}
}
}
Err(anyhow!("could not find file"))
}