diff options
author | Martin Fischer <martin@push-f.com> | 2021-06-24 07:35:47 +0200 |
---|---|---|
committer | Martin Fischer <martin@push-f.com> | 2021-06-24 07:38:48 +0200 |
commit | b3e7c889b40a64db20bc52e579938d9c57f1bc3a (patch) | |
tree | 66b37051754493d58b1e4ec4b74ceb63691f9792 | |
parent | 43b4b8693890a85f24eb358bc5545232ebf8e796 (diff) |
refactor: split up blob routes into functions
-rw-r--r-- | src/get_routes.rs | 553 | ||||
-rw-r--r-- | src/main.rs | 6 |
2 files changed, 305 insertions, 254 deletions
diff --git a/src/get_routes.rs b/src/get_routes.rs index 01332e1..effc68a 100644 --- a/src/get_routes.rs +++ b/src/get_routes.rs @@ -23,296 +23,343 @@ use crate::Error; use crate::Page; use crate::Response; -#[derive(Deserialize)] -struct LogParam { - commit: Option<String>, -} - -pub(crate) fn view_blob<C: Controller>( +pub(crate) fn get_blob<C: Controller>( entr: TreeEntry, params: ActionParam, controller: &C, ctx: Context, ) -> Result<Response, Error> { - let filename = ctx.path.file_name().unwrap().to_str().unwrap(); - match params.action.as_ref() { - "view" => { - let mut page = Page { - title: filename.to_string(), - body: String::new(), - header: Some(action_links(¶ms.action, controller, &ctx)), - controller, - parts: &ctx.parts, - }; - - if let Some(access_info_html) = controller.access_info_html(&ctx) { - page.body.push_str(&access_info_html); - } + "view" => view_blob(entr, params, controller, ctx), + "edit" => edit_blob(entr, params, controller, ctx), + "upload" => Ok(forms::upload_form(true, controller, &ctx).into()), + "log" => log_blob(entr, params, controller, ctx), + "raw" => raw_blob(entr, params, controller, ctx), + "move" => move_blob(entr, params, controller, ctx), + "remove" => remove_blob(entr, params, controller, ctx), + _ => Err(Error::BadRequest("unknown action".into())), + } +} - if entr.filemode() == FileMode::Link.into() { - // TODO: indicate and link symbolic link - } +fn view_blob<C: Controller>( + entr: TreeEntry, + params: ActionParam, + controller: &C, + ctx: Context, +) -> Result<Response, Error> { + let mut page = Page { + title: ctx.file_name().unwrap().to_owned(), + body: String::new(), + header: Some(action_links(¶ms.action, controller, &ctx)), + controller, + parts: &ctx.parts, + }; - let blob = ctx.repo.find_blob(entr.id()).unwrap(); + if let Some(access_info_html) = controller.access_info_html(&ctx) { + page.body.push_str(&access_info_html); + } - if let Some(mime) = mime_guess::from_path(&ctx.path).first() { - if mime.type_() == "image" { - page.body - .push_str("<div class=img-container><img src=?action=raw></div>"); - return Ok(page.into()); - } - } + if entr.filemode() == FileMode::Link.into() { + // TODO: indicate and link symbolic link + } - match from_utf8(blob.content()) { - Ok(text) => { - if let Some(renderer) = get_renderer(&ctx.path) { - page.body.push_str(&renderer(text)); - } else { - page.body - .push_str(&format!("<pre>{}</pre>", html_escape(text))); - } - } - Err(_) => page.body.push_str("failed to decode file as UTF-8"), - } + let blob = ctx.repo.find_blob(entr.id()).unwrap(); - Ok(page.into()) + if let Some(mime) = mime_guess::from_path(&ctx.path).first() { + if mime.type_() == "image" { + page.body + .push_str("<div class=img-container><img src=?action=raw></div>"); + return Ok(page.into()); } - "edit" => { - if !controller.may_write_path(&ctx) { - return Err(Error::Unauthorized( - "you are not authorized to edit this file".into(), - ctx, - )); - } - let blob = ctx.repo.find_blob(entr.id()).unwrap(); - if let Ok(text) = from_utf8(blob.content()) { - return Ok(forms::edit_text_form( - &forms::EditForm { - text: text.to_string(), - oid: Some(entr.id().to_string()), - ..Default::default() - }, - None, - controller, - &ctx, - ) - .into()); + } + + match from_utf8(blob.content()) { + Ok(text) => { + if let Some(renderer) = get_renderer(&ctx.path) { + page.body.push_str(&renderer(text)); } else { - return Ok(forms::upload_form(true, controller, &ctx).into()); + page.body + .push_str(&format!("<pre>{}</pre>", html_escape(text))); } } - "upload" => { - return Ok(forms::upload_form(true, controller, &ctx).into()); + Err(_) => page.body.push_str("failed to decode file as UTF-8"), + } + + Ok(page.into()) +} + +fn edit_blob<C: Controller>( + entr: TreeEntry, + _params: ActionParam, + controller: &C, + ctx: Context, +) -> Result<Response, Error> { + if !controller.may_write_path(&ctx) { + return Err(Error::Unauthorized( + "you are not authorized to edit this file".into(), + ctx, + )); + } + let blob = ctx.repo.find_blob(entr.id()).unwrap(); + if let Ok(text) = from_utf8(blob.content()) { + return Ok(forms::edit_text_form( + &forms::EditForm { + text: text.to_string(), + oid: Some(entr.id().to_string()), + ..Default::default() + }, + None, + controller, + &ctx, + ) + .into()); + } else { + return Ok(forms::upload_form(true, controller, &ctx).into()); + } +} + +#[derive(Deserialize)] +struct LogParam { + commit: Option<String>, +} + +fn log_blob<C: Controller>( + _entr: TreeEntry, + params: ActionParam, + controller: &C, + ctx: Context, +) -> Result<Response, Error> { + let log_param: LogParam = ctx.parts.query().unwrap(); + + let filename = ctx.file_name().unwrap(); + + // TODO: move to separate function + if let Some(commit) = log_param.commit { + let branch_commit = ctx.branch_head()?; + + let commit = ctx + .repo + .find_commit(Oid::from_str(&commit)?) + .map_err(|_| Error::NotFound("commit not found".into()))?; + + if branch_commit.id() != commit.id() + && !ctx + .repo + .graph_descendant_of(branch_commit.id(), commit.id())? + { + // disallow viewing commits from other branches you shouldn't have access to + return Err(Error::NotFound("commit not found".into())); } - "log" => { - let log_param: LogParam = ctx.parts.query().unwrap(); - - if let Some(commit) = log_param.commit { - let branch_commit = ctx.branch_head()?; - - let commit = ctx - .repo - .find_commit(Oid::from_str(&commit)?) - .map_err(|_| Error::NotFound("commit not found".into()))?; - - if branch_commit.id() != commit.id() - && !ctx - .repo - .graph_descendant_of(branch_commit.id(), commit.id())? - { - // disallow viewing commits from other branches you shouldn't have access to - return Err(Error::NotFound("commit not found".into())); - } - let blob_id = if let Ok(entry) = commit.tree()?.get_path(&ctx.path) { - entry.id() - } else { - return Ok(Page { - title: format!("Commit for {}", filename), - body: "file removed".into(), - header: Some(action_links(¶ms.action, controller, &ctx)), - controller, - parts: &ctx.parts, - } - .into()); - }; - - // TODO: if UTF-8 decoding fails, link ?action=raw&rev= - // TODO: what if there are multiple parents? - let old_blob_id = commit - .parents() - .next() - .and_then(|p| p.tree().unwrap().get_path(&ctx.path).ok()) - .map(|e| e.id()); - if Some(blob_id) != old_blob_id { - let mut page = Page { - title: format!("Commit for {}", filename), - header: Some(action_links(¶ms.action, controller, &ctx)), - body: format!( - "<h1>{}</h1>{} committed on {}", - html_escape(commit.summary().unwrap_or_default()), - html_escape(commit.author().name().unwrap_or_default()), - NaiveDateTime::from_timestamp(commit.time().seconds(), 0) - .format("%b %d, %Y, %H:%M") - ), - controller, - parts: &ctx.parts, - }; - if let Some(old_blob_id) = old_blob_id { - page.body.push_str(&diff( - from_utf8(ctx.repo.find_blob(old_blob_id)?.content())?, - from_utf8(ctx.repo.find_blob(blob_id)?.content())?, - )) - } else { - page.body.push_str(&diff( - "", - from_utf8(&ctx.repo.find_blob(blob_id)?.content())?, - )); - } - return Ok(page.into()); - } else { - return Err(Error::NotFound("commit not found".into())); - } + let blob_id = if let Ok(entry) = commit.tree()?.get_path(&ctx.path) { + entry.id() + } else { + return Ok(Page { + title: format!("Commit for {}", filename), + body: "file removed".into(), + header: Some(action_links(¶ms.action, controller, &ctx)), + controller, + parts: &ctx.parts, } + .into()); + }; + // TODO: if UTF-8 decoding fails, link ?action=raw&rev= + // TODO: what if there are multiple parents? + let old_blob_id = commit + .parents() + .next() + .and_then(|p| p.tree().unwrap().get_path(&ctx.path).ok()) + .map(|e| e.id()); + if Some(blob_id) != old_blob_id { let mut page = Page { - title: format!("Log for {}", filename), - body: String::new(), + title: format!("Commit for {}", filename), header: Some(action_links(¶ms.action, controller, &ctx)), + body: format!( + "<h1>{}</h1>{} committed on {}", + html_escape(commit.summary().unwrap_or_default()), + html_escape(commit.author().name().unwrap_or_default()), + NaiveDateTime::from_timestamp(commit.time().seconds(), 0) + .format("%b %d, %Y, %H:%M") + ), controller, parts: &ctx.parts, }; - - let mut walk = ctx.repo.revwalk()?; - let branch_head = ctx.branch_head()?; - walk.push(branch_head.id())?; - - let mut prev_commit = branch_head; - let mut prev_blobid = Some(prev_commit.tree()?.get_path(&ctx.path)?.id()); - - let mut commits = Vec::new(); - - // TODO: paginate - for oid in walk.flatten().skip(1) { - let commit = ctx.repo.find_commit(oid)?; - if let Ok(entr) = commit.tree()?.get_path(&ctx.path) { - let blobid = entr.id(); - if Some(blobid) != prev_blobid { - commits.push(prev_commit); - prev_blobid = Some(blobid); - } - prev_commit = commit; - } else { - if prev_blobid.is_some() { - commits.push(prev_commit); - } - prev_commit = commit; - prev_blobid = None; - } + if let Some(old_blob_id) = old_blob_id { + page.body.push_str(&diff( + from_utf8(ctx.repo.find_blob(old_blob_id)?.content())?, + from_utf8(ctx.repo.find_blob(blob_id)?.content())?, + )) + } else { + page.body.push_str(&diff( + "", + from_utf8(&ctx.repo.find_blob(blob_id)?.content())?, + )); } - if prev_commit.parent_count() == 0 && prev_commit.tree()?.get_path(&ctx.path).is_ok() { - // the very first commit of the branch + return Ok(page.into()); + } else { + return Err(Error::NotFound("commit not found".into())); + } + } + + let mut page = Page { + title: format!("Log for {}", filename), + body: String::new(), + header: Some(action_links(¶ms.action, controller, &ctx)), + controller, + parts: &ctx.parts, + }; + + let mut walk = ctx.repo.revwalk()?; + let branch_head = ctx.branch_head()?; + walk.push(branch_head.id())?; + + let mut prev_commit = branch_head; + let mut prev_blobid = Some(prev_commit.tree()?.get_path(&ctx.path)?.id()); + + let mut commits = Vec::new(); + + // TODO: paginate + for oid in walk.flatten().skip(1) { + let commit = ctx.repo.find_commit(oid)?; + if let Ok(entr) = commit.tree()?.get_path(&ctx.path) { + let blobid = entr.id(); + if Some(blobid) != prev_blobid { commits.push(prev_commit); + prev_blobid = Some(blobid); } - let mut prev_date = None; - for c in commits { - let date = NaiveDateTime::from_timestamp(c.time().seconds(), 0).date(); - if Some(date) != prev_date { - if prev_date != None { - page.body.push_str("</ul>"); - } - page.body - .push_str(&format!("{}<ul>", date.format("%b %d, %Y"))); - } - - page.body.push_str(&format!( - "<li><a href='?action=log&commit={}'>{}: {}</a></li>", - html_escape(c.id().to_string()), - html_escape(c.author().name().unwrap_or_default()), - html_escape(c.summary().unwrap_or_default()), - )); - prev_date = Some(date); + prev_commit = commit; + } else { + if prev_blobid.is_some() { + commits.push(prev_commit); } - page.body.push_str("</ul>"); - Ok(page.into()) + prev_commit = commit; + prev_blobid = None; } - "raw" => { - if let Some(etag) = ctx - .parts - .headers - .get(header::IF_NONE_MATCH) - .and_then(|v| v.to_str().ok()) - { - if etag.trim_matches('"') == entr.id().to_string() { - return Ok(Builder::new() - .status(StatusCode::NOT_MODIFIED) - .body("".into()) - .unwrap()); - } + } + if prev_commit.parent_count() == 0 && prev_commit.tree()?.get_path(&ctx.path).is_ok() { + // the very first commit of the branch + commits.push(prev_commit); + } + let mut prev_date = None; + for c in commits { + let date = NaiveDateTime::from_timestamp(c.time().seconds(), 0).date(); + if Some(date) != prev_date { + if prev_date != None { + page.body.push_str("</ul>"); } + page.body + .push_str(&format!("{}<ul>", date.format("%b %d, %Y"))); + } + + page.body.push_str(&format!( + "<li><a href='?action=log&commit={}'>{}: {}</a></li>", + html_escape(c.id().to_string()), + html_escape(c.author().name().unwrap_or_default()), + html_escape(c.summary().unwrap_or_default()), + )); + prev_date = Some(date); + } + page.body.push_str("</ul>"); + Ok(page.into()) +} - let blob = ctx.repo.find_blob(entr.id()).unwrap(); - let mut resp = Response::new(blob.content().to_owned().into()); +fn raw_blob<C: Controller>( + entr: TreeEntry, + _params: ActionParam, + _controller: &C, + ctx: Context, +) -> Result<Response, Error> { + if let Some(etag) = ctx + .parts + .headers + .get(header::IF_NONE_MATCH) + .and_then(|v| v.to_str().ok()) + { + if etag.trim_matches('"') == entr.id().to_string() { + return Ok(Builder::new() + .status(StatusCode::NOT_MODIFIED) + .body("".into()) + .unwrap()); + } + } + let blob = ctx.repo.find_blob(entr.id()).unwrap(); + let mut resp = Response::new(blob.content().to_owned().into()); + + resp.headers_mut() + .insert(header::ETAG, format!("\"{}\"", entr.id()).parse().unwrap()); + resp.headers_mut() + .insert(header::CACHE_CONTROL, "no-cache".parse().unwrap()); + + if let Some(mime) = mime_guess::from_path(&ctx.path).first() { + if mime.type_() == "text" { + // workaround for Firefox, which downloads non-plain text subtypes + // instead of displaying them (https://bugzilla.mozilla.org/1319262) resp.headers_mut() - .insert(header::ETAG, format!("\"{}\"", entr.id()).parse().unwrap()); + .insert(header::CONTENT_TYPE, "text/plain".parse().unwrap()); + } else { resp.headers_mut() - .insert(header::CACHE_CONTROL, "no-cache".parse().unwrap()); - - if let Some(mime) = mime_guess::from_path(&ctx.path).first() { - if mime.type_() == "text" { - // workaround for Firefox, which downloads non-plain text subtypes - // instead of displaying them (https://bugzilla.mozilla.org/1319262) - resp.headers_mut() - .insert(header::CONTENT_TYPE, "text/plain".parse().unwrap()); - } else { - resp.headers_mut() - .insert(header::CONTENT_TYPE, mime.to_string().parse().unwrap()); - } - } - Ok(resp) - } - "move" => { - if !controller.may_move_path(&ctx) { - return Err(Error::Unauthorized( - "you are not authorized to move this file".into(), - ctx, - )); - } - return forms::move_form( - filename, - &forms::MoveForm { - dest: ctx.path.to_str().unwrap().to_owned(), - msg: None, - }, - None, - controller, - &ctx, - ); + .insert(header::CONTENT_TYPE, mime.to_string().parse().unwrap()); } - "remove" => { - if !controller.may_move_path(&ctx) { - return Err(Error::Unauthorized( - "you are not authorized to remove this file".into(), - ctx, - )); - } - let page = Page { - title: format!("Remove {}", filename), - controller, - parts: &ctx.parts, - header: Some(action_links(¶ms.action, controller, &ctx)), - body: "<form method=post autocomplete=off>\ - <label>Message <input name=msg autofocus></label>\ - <button>Remove</button></form>" - .into(), - }; + } + Ok(resp) +} - Ok(page.into()) - } - _ => Err(Error::BadRequest("unknown action".into())), +fn move_blob<C: Controller>( + _entr: TreeEntry, + _params: ActionParam, + controller: &C, + ctx: Context, +) -> Result<Response, Error> { + if !controller.may_move_path(&ctx) { + return Err(Error::Unauthorized( + "you are not authorized to move this file".into(), + ctx, + )); } + + let filename = ctx.file_name().unwrap(); + + return forms::move_form( + filename, + &forms::MoveForm { + dest: ctx.path.to_str().unwrap().to_owned(), + msg: None, + }, + None, + controller, + &ctx, + ); +} + +fn remove_blob<C: Controller>( + entr: TreeEntry, + params: ActionParam, + controller: &C, + ctx: Context, +) -> Result<Response, Error> { + if !controller.may_move_path(&ctx) { + return Err(Error::Unauthorized( + "you are not authorized to remove this file".into(), + ctx, + )); + } + + let filename = ctx.file_name().unwrap(); + + let page = Page { + title: format!("Remove {}", filename), + controller, + parts: &ctx.parts, + header: Some(action_links(¶ms.action, controller, &ctx)), + body: "<form method=post autocomplete=off>\ + <label>Message <input name=msg autofocus></label>\ + <button>Remove</button></form>" + .into(), + }; + + Ok(page.into()) } pub fn view_tree<C: Controller>( diff --git a/src/main.rs b/src/main.rs index 30f018b..fc0b7ce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -442,7 +442,7 @@ async fn build_response<C: Controller>( .body("redirecting".into()) .unwrap()); } - return get_routes::view_blob(entr, params, controller, ctx); + return get_routes::get_blob(entr, params, controller, ctx); } tree = ctx.repo.find_tree(entr.id()); @@ -528,6 +528,10 @@ impl Context { parent_commits, ) } + + fn file_name(&self) -> Option<&str> { + self.path.file_name().and_then(|x| x.to_str()) + } } fn render_markdown(input: &str) -> String { |