🏡 index : ~doyle/rgit.git

author Jordan Doyle <jordan@doyle.la> 2022-07-21 23:05:18.0 +01:00:00
committer Jordan Doyle <jordan@doyle.la> 2022-07-21 23:05:18.0 +01:00:00
commit
b4c4b875784fa80fbd53b96976320fa858a84eaa [patch]
tree
79b2e2bee644023f234c7685ca446d01a7155010
parent
d56e2f90efed3b851db1abaca16e515b5d0931e3
download
b4c4b875784fa80fbd53b96976320fa858a84eaa.tar.gz

Sled & CGI error handling



Diff

 src/git_cgi.rs                    | 19 ++++++++++++++-----
 src/syntax_highlight.rs           |  4 +---
 src/database/indexer.rs           | 12 +++++++++---
 src/methods/index.rs              |  7 ++++---
 src/methods/repo.rs               | 15 ++++++++++-----
 src/database/schema/repository.rs | 20 ++++++++++++--------
 6 files changed, 44 insertions(+), 33 deletions(-)

diff --git a/src/git_cgi.rs b/src/git_cgi.rs
index 029971a..b425da9 100644
--- a/src/git_cgi.rs
+++ a/src/git_cgi.rs
@@ -1,25 +1,30 @@
use anyhow::{bail, Context, Result};
use axum::body::{boxed, Body};
use axum::http::header::HeaderName;
use axum::http::HeaderValue;
use axum::response::Response;
use httparse::Status;
use std::str::FromStr;

// https://en.wikipedia.org/wiki/Common_Gateway_Interface
pub fn cgi_to_response(buffer: &[u8]) -> Response {
pub fn cgi_to_response(buffer: &[u8]) -> Result<Response> {
    let mut headers = [httparse::EMPTY_HEADER; 10];
    let (body_offset, headers) = httparse::parse_headers(buffer, &mut headers)
        .unwrap()
        .unwrap();
    let (body_offset, headers) = match httparse::parse_headers(buffer, &mut headers)? {
        Status::Complete(v) => v,
        Status::Partial => bail!("Git returned a partial response over CGI"),
    };

    let mut response = Response::new(boxed(Body::from(buffer[body_offset..].to_vec())));

    // TODO: extract status header
    for header in headers {
        response.headers_mut().insert(
            HeaderName::from_str(header.name).unwrap(),
            HeaderValue::from_bytes(header.value).unwrap(),
            HeaderName::from_str(header.name)
                .context("Failed to parse header name from Git over CGI")?,
            HeaderValue::from_bytes(header.value)
                .context("Failed to parse header value from Git over CGI")?,
        );
    }

    response
    Ok(response)
}
diff --git a/src/syntax_highlight.rs b/src/syntax_highlight.rs
index c75068d..c3e1c1e 100644
--- a/src/syntax_highlight.rs
+++ a/src/syntax_highlight.rs
@@ -18,9 +18,7 @@
            ClassedHTMLGenerator::new_with_class_style(syntax, self.syntax_set, ClassStyle::Spaced);

        for line in LinesWithEndings::from(code) {
            html_generator
                .parse_html_for_line_which_includes_newline(line)
                .unwrap();
            let _ = html_generator.parse_html_for_line_which_includes_newline(line);
        }

        format!(
diff --git a/src/database/indexer.rs b/src/database/indexer.rs
index 29ff671..bb25b4c 100644
--- a/src/database/indexer.rs
+++ a/src/database/indexer.rs
@@ -21,8 +21,9 @@
    for repository in discovered {
        let relative = get_relative_path(scan_path, &repository);

        let id =
            Repository::open(db, relative).map_or_else(|| RepositoryId::new(db), |v| v.get().id);
        let id = Repository::open(db, relative)
            .unwrap()
            .map_or_else(|| RepositoryId::new(db), |v| v.get().id);
        let name = relative.file_name().unwrap().to_string_lossy();
        let description = std::fs::read(repository.join("description")).unwrap_or_default();
        let description = Some(String::from_utf8_lossy(&description)).filter(|v| !v.is_empty());
@@ -39,7 +40,7 @@
}

fn update_repository_reflog(scan_path: &Path, db: &sled::Db) {
    for (relative_path, db_repository) in Repository::fetch_all(db) {
    for (relative_path, db_repository) in Repository::fetch_all(db).unwrap() {
        let git_repository = git2::Repository::open(scan_path.join(&relative_path)).unwrap();

        for reference in git_repository.references().unwrap() {
@@ -59,7 +60,10 @@

            info!("Refreshing indexes");

            let commit_tree = db_repository.get().commit_tree(db, &reference_name);
            let commit_tree = db_repository
                .get()
                .commit_tree(db, &reference_name)
                .unwrap();

            if let (Some(latest_indexed), Ok(latest_commit)) =
                (commit_tree.fetch_latest_one(), reference.peel_to_commit())
diff --git a/src/methods/index.rs b/src/methods/index.rs
index 7c92cd9..46e2a64 100644
--- a/src/methods/index.rs
+++ a/src/methods/index.rs
@@ -1,3 +1,4 @@
use anyhow::Context;
use std::collections::BTreeMap;

use askama::Template;
@@ -14,12 +15,12 @@
    pub repositories: BTreeMap<Option<String>, Vec<&'a Repository<'a>>>,
}

pub async fn handle(Extension(db): Extension<sled::Db>) -> Response {
pub async fn handle(Extension(db): Extension<sled::Db>) -> Result<Response, super::repo::Error> {
    let mut repositories: BTreeMap<Option<String>, Vec<&Repository<'_>>> = BTreeMap::new();

    let fetched = tokio::task::spawn_blocking(move || Repository::fetch_all(&db))
        .await
        .unwrap();
        .context("Failed to join Tokio task")??;
    for (k, v) in &fetched {
        // TODO: fixme
        let mut split: Vec<_> = k.split('/').collect();
@@ -30,5 +31,5 @@
        k.push(v.get());
    }

    into_response(&View { repositories })
    Ok(into_response(&View { repositories }))
}
diff --git a/src/methods/repo.rs b/src/methods/repo.rs
index 9fe2ea4..dd7cb13 100644
--- a/src/methods/repo.rs
+++ a/src/methods/repo.rs
@@ -1,3 +1,4 @@
use anyhow::Context;
use std::{
    fmt::{Debug, Display, Formatter},
    io::Write,
@@ -175,8 +176,9 @@
    let open_repo = git.repo(repository_path).await?;
    let refs = open_repo.refs().await?;

    let repository = crate::database::schema::repository::Repository::open(&db, &*repo).unwrap();
    let commit_tree = repository.get().commit_tree(&db, "refs/heads/master");
    let repository = crate::database::schema::repository::Repository::open(&db, &*repo)?
        .context("Repository does not exist")?;
    let commit_tree = repository.get().commit_tree(&db, "refs/heads/master")?;
    let commits = commit_tree.fetch_latest(11, 0).await;
    let commit_list = commits.iter().map(Yoke::get).collect();

@@ -237,8 +239,9 @@
    let offset = query.offset.unwrap_or(0);

    let reference = format!("refs/heads/{}", query.branch.as_deref().unwrap_or("master"));
    let repository = crate::database::schema::repository::Repository::open(&db, &*repo).unwrap();
    let commit_tree = repository.get().commit_tree(&db, &reference);
    let repository = crate::database::schema::repository::Repository::open(&db, &*repo)?
        .context("Repository does not exist")?;
    let commit_tree = repository.get().commit_tree(&db, &reference)?;
    let mut commits = commit_tree.fetch_latest(101, offset).await;

    let next_offset = if commits.len() == 101 {
@@ -277,7 +280,7 @@
        .output()
        .unwrap();

    Ok(crate::git_cgi::cgi_to_response(&out.stdout))
    Ok(crate::git_cgi::cgi_to_response(&out.stdout)?)
}

pub async fn handle_git_upload_pack(
@@ -299,7 +302,7 @@
    child.stdin.as_mut().unwrap().write_all(&body).unwrap();
    let out = child.wait_with_output().unwrap();

    Ok(crate::git_cgi::cgi_to_response(&out.stdout))
    Ok(crate::git_cgi::cgi_to_response(&out.stdout)?)
}

#[derive(Template)]
diff --git a/src/database/schema/repository.rs b/src/database/schema/repository.rs
index b8e9046..2b61783 100644
--- a/src/database/schema/repository.rs
+++ a/src/database/schema/repository.rs
@@ -1,6 +1,7 @@
use crate::database::schema::commit::CommitTree;
use crate::database::schema::prefixes::TreePrefix;
use crate::database::schema::Yoked;
use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use sled::IVec;
use std::borrow::Cow;
@@ -31,7 +32,7 @@
pub type YokedRepository = Yoked<Repository<'static>>;

impl Repository<'_> {
    pub fn fetch_all(database: &sled::Db) -> BTreeMap<String, YokedRepository> {
    pub fn fetch_all(database: &sled::Db) -> Result<BTreeMap<String, YokedRepository>> {
        database
            .scan_prefix([TreePrefix::Repository as u8])
            .filter_map(Result::ok)
@@ -45,10 +46,9 @@
                let value = Box::new(value);

                let value =
                    Yoke::try_attach_to_cart(value, |data: &IVec| bincode::deserialize(data))
                        .unwrap();
                    Yoke::try_attach_to_cart(value, |data: &IVec| bincode::deserialize(data))?;

                (key, value)
                Ok((key, value))
            })
            .collect()
    }
@@ -62,10 +62,10 @@
            .unwrap();
    }

    pub fn open<P: AsRef<Path>>(database: &sled::Db, path: P) -> Option<YokedRepository> {
    pub fn open<P: AsRef<Path>>(database: &sled::Db, path: P) -> Result<Option<YokedRepository>> {
        database
            .get(TreePrefix::repository_id(path))
            .unwrap()
            .context("Failed to open indexed repository")?
            .map(|value| {
                // internally value is an Arc so it should already be stablederef but because
                // of reasons unbeknownst to me, sled has its own Arc implementation so we need
@@ -73,17 +73,17 @@
                let value = Box::new(value);

                Yoke::try_attach_to_cart(value, |data: &IVec| bincode::deserialize(data))
                    .context("Failed to deserialise indexed repository")
            })
            .transpose()
            .unwrap()
    }

    pub fn commit_tree(&self, database: &sled::Db, reference: &str) -> CommitTree {
    pub fn commit_tree(&self, database: &sled::Db, reference: &str) -> Result<CommitTree> {
        let tree = database
            .open_tree(TreePrefix::commit_id(self.id, reference))
            .unwrap();
            .context("Failed to open commit tree")?;

        CommitTree::new(tree)
        Ok(CommitTree::new(tree))
    }
}