aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
authorMartin Fischer <martin@push-f.com>2021-06-24 19:04:23 +0200
committerMartin Fischer <martin@push-f.com>2021-06-24 19:41:05 +0200
commit26298bcd7ef204db4396ca2d0e603fc183220cd2 (patch)
tree9478019c3a365cb71b7cc41e12885ad25d7592b5 /src/main.rs
parentd49d835e63ec654e3a5bf75b3b365354460382e8 (diff)
refactor: simplify Page and Context structs
Previously the Page struct contained references to the Controller and the http::request::Parts, so that page.render() could call controller.user_info_html(parts). This commit removes these references from the Page struct, so that it can implement Default in the future. The Context struct needs to be moved around since it contains git2::Repository, which isn't Send. Previously the Context struct also contained the http::request::Parts, so they were moved along. This commit extracts Parts out of the Context struct, so that our service function can access Parts after invoking our build_request method, allowing us to easily log request details for errors in the future.
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs124
1 files changed, 70 insertions, 54 deletions
diff --git a/src/main.rs b/src/main.rs
index 276a3e2..7313a6d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -48,7 +48,24 @@ mod get_routes;
mod post_routes;
mod shares;
-pub(crate) type Response = hyper::Response<hyper::Body>;
+pub enum Response {
+ Raw(HyperResponse),
+ Page(Page),
+}
+
+impl From<Page> for Response {
+ fn from(page: Page) -> Self {
+ Self::Page(page)
+ }
+}
+
+impl From<HyperResponse> for Response {
+ fn from(resp: HyperResponse) -> Self {
+ Self::Raw(resp)
+ }
+}
+
+pub(crate) type HyperResponse = hyper::Response<hyper::Body>;
pub(crate) type Request = hyper::Request<hyper::Body>;
#[derive(Clap, Debug)]
@@ -194,11 +211,18 @@ async fn service<C: Controller>(
controller: Arc<C>,
args: Arc<Args>,
request: Request,
-) -> Result<Response, Infallible> {
- let (parts, body) = request.into_parts();
+) -> Result<HyperResponse, Infallible> {
+ let (mut parts, body) = request.into_parts();
- let mut resp = build_response(args, &*controller, parts, body)
+ let mut resp = build_response(args, &*controller, &mut parts, body)
.await
+ .map(|resp| match resp {
+ Response::Raw(resp) => resp,
+ Response::Page(page) => Builder::new()
+ .content_type(mime::TEXT_HTML)
+ .body(render_page(&page, &*controller, &parts).into())
+ .unwrap(),
+ })
.unwrap_or_else(|err| {
let (status, message) = match err {
Error::BadRequest(msg) => (400, msg),
@@ -243,48 +267,35 @@ async fn service<C: Controller>(
Ok(resp)
}
-pub struct Page<'a> {
+pub struct Page {
title: String,
header: Option<String>,
body: String,
- controller: &'a dyn Controller,
- parts: &'a Parts,
-}
-
-impl From<Page<'_>> for Response {
- fn from(page: Page) -> Self {
- Builder::new()
- .content_type(mime::TEXT_HTML)
- .body(page.render().into())
- .unwrap()
- }
}
const CSS: &str = include_str!("static/style.css");
-impl Page<'_> {
- fn render(&self) -> String {
- format!(
- "<!doctype html>\
- <html>\
- <head>\
- <meta charset=utf-8>\
- <title>{}</title>\
- <meta name=viewport content=\"width=device-width, initial-scale=1\">\
- <style>{}</style>\
- </head>\
- <body><header id=header>{}{}</header>{}</body></html>\
- ",
- html_escape(&self.title),
- CSS,
- self.header.as_deref().unwrap_or_default(),
- self.controller
- .user_info_html(self.parts)
- .map(|h| format!("<div class=user-info>{}</div>", h))
- .unwrap_or_default(),
- self.body,
- )
- }
+fn render_page<C: Controller>(page: &Page, controller: &C, parts: &Parts) -> String {
+ format!(
+ "<!doctype html>\
+ <html>\
+ <head>\
+ <meta charset=utf-8>\
+ <title>{}</title>\
+ <meta name=viewport content=\"width=device-width, initial-scale=1\">\
+ <style>{}</style>\
+ </head>\
+ <body><header id=header>{}{}</header>{}</body></html>\
+ ",
+ html_escape(&page.title),
+ CSS,
+ page.header.as_deref().unwrap_or_default(),
+ controller
+ .user_info_html(parts)
+ .map(|h| format!("<div class=user-info>{}</div>", h))
+ .unwrap_or_default(),
+ page.body,
+ )
}
#[derive(Deserialize)]
@@ -309,7 +320,7 @@ impl Branch {
async fn build_response<C: Controller>(
args: Arc<Args>,
controller: &C,
- parts: Parts,
+ parts: &mut Parts,
body: Body,
) -> Result<Response, Error> {
let unsanitized_path = percent_decode_str(parts.uri.path())
@@ -346,17 +357,16 @@ async fn build_response<C: Controller>(
repo,
path: url_path,
branch: rev,
- parts,
};
- if !controller.may_read_path(&ctx) {
+ if !controller.may_read_path(&ctx, parts) {
return Err(Error::Unauthorized(
"you are not authorized to view this file".into(),
));
}
- if ctx.parts.method == Method::POST {
- return post_routes::build_response(&args, &params, controller, ctx, body).await;
+ if parts.method == Method::POST {
+ return post_routes::build_response(&args, &params, controller, ctx, body, parts).await;
}
let mut tree = ctx
@@ -372,17 +382,18 @@ async fn build_response<C: Controller>(
return Err(Error::NotFound("directory not found".into()));
}
- if controller.may_write_path(&ctx) {
+ if controller.may_write_path(&ctx, parts) {
if params.action == "edit" {
return Ok(forms::edit_text_form(
&forms::EditForm::default(),
None,
controller,
&ctx,
+ parts,
)
.into());
} else if params.action == "upload" {
- return Ok(forms::upload_form(false, controller, &ctx).into());
+ return Ok(forms::upload_form(false, controller, &ctx, parts).into());
} else {
return Err(Error::NotFound(
"file not found, but <a href=?action=edit>you can write it</a> or <a href=?action=upload>upload it</a>".into(),
@@ -404,18 +415,19 @@ async fn build_response<C: Controller>(
.build_url_path(&ctx.branch, unsanitized_path.trim_end_matches('/')),
)
.body("redirecting".into())
- .unwrap());
+ .unwrap()
+ .into());
}
- return get_routes::get_blob(entr, params, controller, ctx);
+ return get_routes::get_blob(entr, params, controller, ctx, &parts);
}
tree = ctx.repo.find_tree(entr.id());
if !unsanitized_path.ends_with('/') {
- return Err(Error::MissingTrailingSlash(ctx.parts.uri.path().to_owned()));
+ return Err(Error::MissingTrailingSlash(parts.uri.path().to_owned()));
}
}
- get_routes::view_tree(tree, controller, &ctx)
+ get_routes::view_tree(tree, controller, &ctx, &parts)
}
fn render_link(name: &str, label: &str, active_action: &str) -> String {
@@ -435,18 +447,23 @@ fn render_link(name: &str, label: &str, active_action: &str) -> String {
)
}
-fn action_links<C: Controller>(active_action: &str, controller: &C, ctx: &Context) -> String {
+fn action_links<C: Controller>(
+ active_action: &str,
+ controller: &C,
+ ctx: &Context,
+ parts: &Parts,
+) -> String {
let mut out = String::new();
out.push_str("<div class=actions>");
out.push_str("<a href=. title='list parent directory'>ls</a>");
out.push_str(&render_link("view", "view", active_action));
- if controller.may_write_path(ctx) {
+ if controller.may_write_path(ctx, parts) {
out.push_str(&render_link("edit", "edit", active_action));
}
out.push_str(&render_link("log", "log", active_action));
out.push_str(&render_link("raw", "raw", active_action));
- if controller.may_move_path(ctx) {
+ if controller.may_move_path(ctx, parts) {
out.push_str(&render_link("move", "mv", active_action));
out.push_str(&render_link("remove", "rm", active_action));
}
@@ -456,7 +473,6 @@ fn action_links<C: Controller>(active_action: &str, controller: &C, ctx: &Contex
pub struct Context {
repo: Repository,
- parts: Parts,
branch: Branch,
path: PathBuf,
}