aboutsummaryrefslogtreecommitdiffstats
path: root/pkgs/by-name/ba/back/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--pkgs/by-name/ba/back/src/cli.rs24
-rw-r--r--pkgs/by-name/ba/back/src/config/mod.rs72
-rw-r--r--pkgs/by-name/ba/back/src/error/mod.rs94
-rw-r--r--pkgs/by-name/ba/back/src/error/responder.rs23
-rw-r--r--pkgs/by-name/ba/back/src/git_bug/dag/mod.rs143
-rw-r--r--pkgs/by-name/ba/back/src/git_bug/format/mod.rs144
-rw-r--r--pkgs/by-name/ba/back/src/git_bug/issue/entity/mod.rs78
-rw-r--r--pkgs/by-name/ba/back/src/git_bug/issue/identity/mod.rs79
-rw-r--r--pkgs/by-name/ba/back/src/git_bug/issue/label/mod.rs85
-rw-r--r--pkgs/by-name/ba/back/src/git_bug/issue/mod.rs185
-rw-r--r--pkgs/by-name/ba/back/src/git_bug/issue/operation/mod.rs124
-rw-r--r--pkgs/by-name/ba/back/src/git_bug/issue/operation/operation_type.rs51
-rw-r--r--pkgs/by-name/ba/back/src/git_bug/mod.rs28
-rw-r--r--pkgs/by-name/ba/back/src/main.rs52
-rw-r--r--pkgs/by-name/ba/back/src/web/issue_html.rs166
-rw-r--r--pkgs/by-name/ba/back/src/web/mod.rs186
-rw-r--r--pkgs/by-name/ba/back/src/web/prefix.rs35
17 files changed, 0 insertions, 1569 deletions
diff --git a/pkgs/by-name/ba/back/src/cli.rs b/pkgs/by-name/ba/back/src/cli.rs
deleted file mode 100644
index 79f0d63..0000000
--- a/pkgs/by-name/ba/back/src/cli.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-// Back - An extremely simple git issue tracking system. Inspired by tvix's
-// panettone
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: AGPL-3.0-or-later
-//
-// This file is part of Back.
-//
-// You should have received a copy of the License along with this program.
-// If not, see <https://www.gnu.org/licenses/agpl.txt>.
-
-use std::path::PathBuf;
-
-use clap::Parser;
-
-#[derive(Parser, Debug)]
-#[clap(author, version, about, long_about = None)]
-#[allow(clippy::module_name_repetitions)]
-/// An extremely simple git issue tracking system.
-/// Inspired by tvix's panettone
-pub struct Cli {
- /// The path to the configuration file. The file should be written in JSON.
- pub config_file: PathBuf,
-}
diff --git a/pkgs/by-name/ba/back/src/config/mod.rs b/pkgs/by-name/ba/back/src/config/mod.rs
deleted file mode 100644
index 7351ad8..0000000
--- a/pkgs/by-name/ba/back/src/config/mod.rs
+++ /dev/null
@@ -1,72 +0,0 @@
-// Back - An extremely simple git issue tracking system. Inspired by tvix's
-// panettone
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: AGPL-3.0-or-later
-//
-// This file is part of Back.
-//
-// You should have received a copy of the License along with this program.
-// If not, see <https://www.gnu.org/licenses/agpl.txt>.
-
-use std::{
- fs,
- path::{Path, PathBuf},
-};
-
-use gix::ThreadSafeRepository;
-use serde::Deserialize;
-use url::Url;
-
-use crate::error::{self, Error};
-
-pub struct BackConfig {
- // NOTE(@bpeetz): We do not need to html escape this, as the value must be a valid url. As such
- // `<tags>` of all kinds _should_ be invalid. <2024-12-26>
- pub source_code_repository_url: Url,
- pub repository: ThreadSafeRepository,
- pub root: Url,
-}
-
-#[derive(Deserialize)]
-struct RawBackConfig {
- source_code_repository_url: Url,
- repository_path: PathBuf,
- root_url: Url,
-}
-
-impl BackConfig {
- pub fn from_config_file(path: &Path) -> error::Result<Self> {
- let value = fs::read_to_string(path).map_err(|err| Error::ConfigRead {
- file: path.to_owned(),
- error: err,
- })?;
-
- let raw: RawBackConfig =
- serde_json::from_str(&value).map_err(|err| Error::ConfigParse {
- file: path.to_owned(),
- error: err,
- })?;
-
- Self::try_from(raw)
- }
-}
-
-impl TryFrom<RawBackConfig> for BackConfig {
- type Error = error::Error;
-
- fn try_from(value: RawBackConfig) -> Result<Self, Self::Error> {
- let repository = {
- ThreadSafeRepository::open(&value.repository_path).map_err(|err| Error::RepoOpen {
- repository_path: value.repository_path,
- error: Box::new(err),
- })
- }?;
-
- Ok(Self {
- repository,
- source_code_repository_url: value.source_code_repository_url,
- root: value.root_url,
- })
- }
-}
diff --git a/pkgs/by-name/ba/back/src/error/mod.rs b/pkgs/by-name/ba/back/src/error/mod.rs
deleted file mode 100644
index 8b71700..0000000
--- a/pkgs/by-name/ba/back/src/error/mod.rs
+++ /dev/null
@@ -1,94 +0,0 @@
-// Back - An extremely simple git issue tracking system. Inspired by tvix's
-// panettone
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: AGPL-3.0-or-later
-//
-// This file is part of Back.
-//
-// You should have received a copy of the License along with this program.
-// If not, see <https://www.gnu.org/licenses/agpl.txt>.
-
-use std::{fmt::Display, io, path::PathBuf};
-
-use thiserror::Error;
-
-use crate::web::prefix::BackPrefix;
-
-pub type Result<T> = std::result::Result<T, Error>;
-
-pub mod responder;
-
-#[derive(Error, Debug)]
-pub enum Error {
- ConfigParse {
- file: PathBuf,
- error: serde_json::Error,
- },
- ConfigRead {
- file: PathBuf,
- error: io::Error,
- },
- RocketLaunch(#[from] rocket::Error),
-
- RepoOpen {
- repository_path: PathBuf,
- error: Box<gix::open::Error>,
- },
- RepoRefsIter(#[from] gix::refs::packed::buffer::open::Error),
- RepoRefsPrefixed(#[from] std::io::Error),
-
- IssuesPrefixMissing {
- prefix: BackPrefix,
- },
- IssuesPrefixParse(#[from] gix::hash::prefix::from_hex::Error),
-}
-
-impl Display for Error {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- Error::ConfigParse { file, error } => {
- write!(
- f,
- "while trying to parse the config file ({}): {error}",
- file.display()
- )
- }
- Error::ConfigRead { file, error } => {
- write!(
- f,
- "while trying to read the config file ({}): {error}",
- file.display()
- )
- }
- Error::RocketLaunch(error) => {
- write!(f, "while trying to start back: {error}")
- }
- Error::RepoOpen {
- repository_path,
- error,
- } => {
- write!(
- f,
- "while trying to open the repository ({}): {error}",
- repository_path.display()
- )
- }
- Error::RepoRefsIter(error) => {
- write!(f, "while iteration over the refs in a repository: {error}",)
- }
- Error::RepoRefsPrefixed(error) => {
- write!(f, "while prefixing the refs with a path: {error}")
- }
- Error::IssuesPrefixMissing { prefix } => {
- write!(
- f,
- "There is no 'issue' associated with the prefix: {prefix}"
- )
- }
- Error::IssuesPrefixParse(error) => {
- write!(f, "The given prefix can not be parsed as prefix: {error}")
- }
- }
- }
-}
diff --git a/pkgs/by-name/ba/back/src/error/responder.rs b/pkgs/by-name/ba/back/src/error/responder.rs
deleted file mode 100644
index 7bea961..0000000
--- a/pkgs/by-name/ba/back/src/error/responder.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Back - An extremely simple git issue tracking system. Inspired by tvix's
-// panettone
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: AGPL-3.0-or-later
-//
-// This file is part of Back.
-//
-// You should have received a copy of the License along with this program.
-// If not, see <https://www.gnu.org/licenses/agpl.txt>.
-
-use rocket::{
- response::{self, Responder, Response},
- Request,
-};
-
-use super::Error;
-
-impl<'r> Responder<'r, 'static> for Error {
- fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> {
- Response::build_from(self.to_string().respond_to(req)?).ok()
- }
-}
diff --git a/pkgs/by-name/ba/back/src/git_bug/dag/mod.rs b/pkgs/by-name/ba/back/src/git_bug/dag/mod.rs
deleted file mode 100644
index 9c158a7..0000000
--- a/pkgs/by-name/ba/back/src/git_bug/dag/mod.rs
+++ /dev/null
@@ -1,143 +0,0 @@
-// Back - An extremely simple git issue tracking system. Inspired by tvix's
-// panettone
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: AGPL-3.0-or-later
-//
-// This file is part of Back.
-//
-// You should have received a copy of the License along with this program.
-// If not, see <https://www.gnu.org/licenses/agpl.txt>.
-
-use std::path::Path;
-
-use gix::{bstr::ByteSlice, refs::Target, Commit, Id, ObjectId, Repository};
-
-use crate::error;
-
-use super::issue::{
- entity::{Entity, RawEntity},
- CollapsedIssue, RawCollapsedIssue,
-};
-
-#[derive(Debug)]
-pub struct Dag {
- entities: Vec<Entity>,
-}
-
-impl Dag {
- pub fn collapse(self) -> CollapsedIssue {
- let raw_collapsed_issue = self.entities.into_iter().rev().fold(
- RawCollapsedIssue::default(),
- |mut collapsed_issue, entity| {
- collapsed_issue.append_entity(entity);
- collapsed_issue
- },
- );
-
- CollapsedIssue::from(raw_collapsed_issue)
- }
-
- /// Construct a DAG from the root child upwards.
- pub fn construct(repo: &Repository, id: ObjectId) -> Self {
- let mut entities = vec![];
-
- let base_commit = repo
- .find_object(id)
- .expect("The object with this id should exist.")
- .try_into_commit()
- .expect("The git-bug's data model enforces this.");
-
- entities.push(Self::commit_to_operations(repo, &base_commit));
-
- let mut current_commit = base_commit;
- while let Some(parent_id) = Self::try_get_parent(repo, &current_commit) {
- entities.push(Self::commit_to_operations(repo, &parent_id));
- current_commit = parent_id;
- }
-
- Self {
- entities: {
- entities
- .into_iter()
- .map(|(raw_entity, id)| Entity::from_raw(repo, raw_entity, id))
- .collect()
- },
- }
- }
-
- fn commit_to_operations<'b>(repo: &Repository, id: &Commit<'b>) -> (RawEntity, Id<'b>) {
- let tree_obj = repo
- .find_object(
- id.tree_id()
- .expect("All of git-bug's commits should have trees attached to them'"),
- )
- .expect("The object with this id should exist.")
- .try_into_tree()
- .expect("git-bug's data model enforces this.");
-
- let ops_ref = tree_obj
- .find_entry("ops")
- .expect("All of git-bug's trees should contain a 'ops' json file");
-
- let issue_data = repo
- .find_object(ops_ref.object_id())
- .expect("The object with this id should exist.")
- .try_into_blob()
- .expect("The git-bug's data model enforces this.")
- .data
- .clone();
-
- let operations = serde_json::from_str(
- issue_data
- .to_str()
- .expect("git-bug's ensures, that this is valid json."),
- )
- .expect("The returned json should be valid");
-
- (operations, id.id())
- }
-
- fn try_get_parent<'a>(repo: &'a Repository, base_commit: &Commit<'a>) -> Option<Commit<'a>> {
- let count = base_commit.parent_ids().count();
-
- match count {
- 0 => None,
- 1 => {
- let parent = base_commit.parent_ids().last().expect("One does exist");
-
- let parent_id = parent.object().expect("The object exists").id;
- Some(
- repo.find_object(parent_id)
- .expect("This is a valid id")
- .try_into_commit()
- .expect("This should be a commit"),
- )
- }
- other => {
- unreachable!(
- "Each commit, used by git-bug should only have one parent, but found: {other}"
- );
- }
- }
- }
-}
-
-pub fn issues_from_repository(repo: &Repository) -> error::Result<Vec<Dag>> {
- let dags = repo
- .refs
- .iter()?
- .prefixed(Path::new("refs/bugs/"))?
- .map(|val| {
- let reference = val.expect("All `git-bug` references in 'refs/bugs' should be objects");
-
- if let Target::Object(id) = reference.target {
- Dag::construct(repo, id)
- } else {
- unreachable!("All 'refs/bugs/' should contain a clear target.");
- }
- })
- .collect::<Vec<Dag>>();
-
- Ok(dags)
-}
diff --git a/pkgs/by-name/ba/back/src/git_bug/format/mod.rs b/pkgs/by-name/ba/back/src/git_bug/format/mod.rs
deleted file mode 100644
index b3b6bcc..0000000
--- a/pkgs/by-name/ba/back/src/git_bug/format/mod.rs
+++ /dev/null
@@ -1,144 +0,0 @@
-// Back - An extremely simple git issue tracking system. Inspired by tvix's
-// panettone
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: AGPL-3.0-or-later
-//
-// This file is part of Back.
-//
-// You should have received a copy of the License along with this program.
-// If not, see <https://www.gnu.org/licenses/agpl.txt>.
-
-use std::fmt::Display;
-
-use chrono::DateTime;
-use markdown::to_html;
-use serde::Deserialize;
-use serde_json::Value;
-
-#[derive(Debug, Default, Clone)]
-/// Markdown content.
-pub struct MarkDown {
- value: String,
-}
-
-impl Display for MarkDown {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.write_str(to_html(&self.value).as_str())
- }
-}
-impl From<&Value> for MarkDown {
- fn from(value: &Value) -> Self {
- Self {
- value: value.as_str().expect("This will exist").to_owned(),
- }
- }
-}
-
-/// An UNIX time stamp.
-///
-/// These should only ever be used for human-display, because timestamps are unreliably in a
-/// distributed system.
-/// Because of this reason, there is no `value()` function.
-#[derive(Debug, Default, Clone, Copy)]
-pub struct TimeStamp {
- value: u64,
-}
-impl From<&Value> for TimeStamp {
- fn from(value: &Value) -> Self {
- Self {
- value: value.as_u64().expect("This must exist"),
- }
- }
-}
-impl Display for TimeStamp {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- let date =
- DateTime::from_timestamp(self.value as i64, 0).expect("This timestamp should be vaild");
-
- let newdate = date.format("%Y-%m-%d %H:%M:%S");
- f.write_str(newdate.to_string().as_str())
- }
-}
-
-/// An UNIX time stamp.
-///
-/// These should only ever be used for human-display, because timestamps are unreliably in a
-/// distributed system.
-///
-/// This one allows underlying access to it's value and is only obtainable via `unsafe` code.
-/// The reason behind this is, that you might need to access this to improve the display for humans
-/// (i.e., sorting by date).
-#[derive(Debug, Default, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
-pub struct UnsafeTimeStamp {
- value: u64,
-}
-impl TimeStamp {
- /// # Safety
- /// This is not really unsafe, but there is just no way your can trust a time stamp in a
- /// distributed system. As such access to the raw value could lead to bugs.
- pub unsafe fn to_unsafe(self) -> UnsafeTimeStamp {
- UnsafeTimeStamp { value: self.value }
- }
-}
-
-#[derive(Debug, Default, Deserialize, Clone, PartialEq, Eq)]
-/// A string that should be escaped when injected into html content.
-pub struct HtmlString {
- value: String,
-}
-
-impl From<MarkDown> for HtmlString {
- fn from(value: MarkDown) -> Self {
- Self { value: value.value }
- }
-}
-
-impl From<&Value> for HtmlString {
- fn from(value: &Value) -> Self {
- Self {
- value: value.as_str().expect("This will exist").to_owned(),
- }
- }
-}
-impl Display for HtmlString {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.write_str(escape_html(&self.value).as_str())
- }
-}
-
-// From `tera::escape_html`
-/// Escape HTML following [OWASP](https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet)
-///
-/// Escape the following characters with HTML entity encoding to prevent switching
-/// into any execution context, such as script, style, or event handlers. Using
-/// hex entities is recommended in the spec. In addition to the 5 characters
-/// significant in XML (&, <, >, ", '), the forward slash is included as it helps
-/// to end an HTML entity.
-///
-/// ```text
-/// & --> &amp;
-/// < --> &lt;
-/// > --> &gt;
-/// " --> &quot;
-/// ' --> &#x27; &apos; is not recommended
-/// / --> &#x2F; forward slash is included as it helps end an HTML entity
-/// ```
-#[inline]
-pub fn escape_html(input: &str) -> String {
- let mut output = String::with_capacity(input.len() * 2);
- for c in input.chars() {
- match c {
- '&' => output.push_str("&amp;"),
- '<' => output.push_str("&lt;"),
- '>' => output.push_str("&gt;"),
- '"' => output.push_str("&quot;"),
- '\'' => output.push_str("&#x27;"),
- '/' => output.push_str("&#x2F;"),
- _ => output.push(c),
- }
- }
-
- // Not using shrink_to_fit() on purpose
- output
-}
diff --git a/pkgs/by-name/ba/back/src/git_bug/issue/entity/mod.rs b/pkgs/by-name/ba/back/src/git_bug/issue/entity/mod.rs
deleted file mode 100644
index f2e9af0..0000000
--- a/pkgs/by-name/ba/back/src/git_bug/issue/entity/mod.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-// Back - An extremely simple git issue tracking system. Inspired by tvix's
-// panettone
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: AGPL-3.0-or-later
-//
-// This file is part of Back.
-//
-// You should have received a copy of the License along with this program.
-// If not, see <https://www.gnu.org/licenses/agpl.txt>.
-
-use std::fmt::Display;
-
-use gix::Repository;
-use serde::Deserialize;
-use serde_json::Value;
-
-use super::{
- identity::{Author, RawAuthor},
- operation::Operation,
-};
-
-#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
-#[serde(from = "Value")]
-pub struct Id {
- value: String,
-}
-impl From<Value> for Id {
- fn from(value: Value) -> Self {
- Self::from(&value)
- }
-}
-impl From<&Value> for Id {
- fn from(value: &Value) -> Self {
- Self {
- value: value.as_str().expect("This should be a string").to_owned(),
- }
- }
-}
-impl From<gix::Id<'_>> for Id {
- fn from(value: gix::Id<'_>) -> Self {
- Self {
- value: value.shorten().expect("This should work?").to_string(),
- }
- }
-}
-impl Display for Id {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- self.value.fmt(f)
- // let shortend = self.value.shorten().expect("This should work.");
- // f.write_str(shortend.to_string().as_str())
- }
-}
-
-#[derive(Debug)]
-pub struct Entity {
- pub id: Id,
- pub author: Author,
- pub operations: Vec<Operation>,
-}
-
-impl Entity {
- pub fn from_raw<'a>(repo: &'a Repository, raw: RawEntity, id: gix::Id<'a>) -> Self {
- Self {
- id: Id::from(id),
- author: Author::construct(repo, raw.author),
- operations: raw.operations,
- }
- }
-}
-
-#[derive(Deserialize)]
-pub struct RawEntity {
- pub author: RawAuthor,
-
- #[serde(alias = "ops")]
- pub operations: Vec<Operation>,
-}
diff --git a/pkgs/by-name/ba/back/src/git_bug/issue/identity/mod.rs b/pkgs/by-name/ba/back/src/git_bug/issue/identity/mod.rs
deleted file mode 100644
index bbf483c..0000000
--- a/pkgs/by-name/ba/back/src/git_bug/issue/identity/mod.rs
+++ /dev/null
@@ -1,79 +0,0 @@
-// Back - An extremely simple git issue tracking system. Inspired by tvix's
-// panettone
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: AGPL-3.0-or-later
-//
-// This file is part of Back.
-//
-// You should have received a copy of the License along with this program.
-// If not, see <https://www.gnu.org/licenses/agpl.txt>.
-
-use std::fmt::Display;
-
-use gix::{bstr::ByteSlice, Repository};
-use serde::Deserialize;
-use serde_json::Value;
-
-use crate::{get, git_bug::format::HtmlString};
-
-use super::entity::Id;
-
-#[derive(Debug, Clone)]
-pub struct Author {
- pub name: HtmlString,
- pub email: HtmlString,
- pub id: Id,
-}
-
-impl Display for Author {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "({}, {})", self.name, self.email)
- }
-}
-
-impl Author {
- pub fn construct(repo: &Repository, raw: RawAuthor) -> Self {
- let commit_obj = repo
- .find_reference(&format!("refs/identities/{}", raw.id))
- .expect("All authors should also have identities")
- .peel_to_commit()
- .expect("All identities should be commits");
-
- let tree_obj = repo
- .find_tree(
- commit_obj
- .tree()
- .expect("The commit should have an tree associated with it")
- .id,
- )
- .expect("This should be a tree");
-
- let data = repo
- .find_blob(
- tree_obj
- .find_entry("version")
- .expect("This entry should exist")
- .object()
- .expect("This should point to a blob entry")
- .id,
- )
- .expect("This blob should exist")
- .data
- .clone();
-
- let json: Value = serde_json::from_str(data.to_str().expect("This is encoded json"))
- .expect("This is valid json");
-
- Author {
- name: get! {json, "name"},
- email: get! {json, "email"},
- id: raw.id,
- }
- }
-}
-
-#[derive(Deserialize)]
-pub struct RawAuthor {
- id: Id,
-}
diff --git a/pkgs/by-name/ba/back/src/git_bug/issue/label/mod.rs b/pkgs/by-name/ba/back/src/git_bug/issue/label/mod.rs
deleted file mode 100644
index a971234..0000000
--- a/pkgs/by-name/ba/back/src/git_bug/issue/label/mod.rs
+++ /dev/null
@@ -1,85 +0,0 @@
-// Back - An extremely simple git issue tracking system. Inspired by tvix's
-// panettone
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: AGPL-3.0-or-later
-//
-// This file is part of Back.
-//
-// You should have received a copy of the License along with this program.
-// If not, see <https://www.gnu.org/licenses/agpl.txt>.
-
-use std::fmt::Display;
-
-use serde::Deserialize;
-use sha2::{Digest, Sha256};
-
-use crate::git_bug::format::HtmlString;
-
-#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
-pub struct Label {
- value: HtmlString,
-}
-
-impl Display for Label {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- self.value.fmt(f)
- }
-}
-
-impl Label {
- /// RGBA from a Label computed in a deterministic way
- /// This is taken completely from `git_bug`
- pub fn associate_color(&self) -> Color {
- // colors from: https://material-ui.com/style/color/
- let colors = vec![
- Color::from_rgba(244, 67, 54, 255), // red
- Color::from_rgba(233, 30, 99, 255), // pink
- Color::from_rgba(156, 39, 176, 255), // purple
- Color::from_rgba(103, 58, 183, 255), // deepPurple
- Color::from_rgba(63, 81, 181, 255), // indigo
- Color::from_rgba(33, 150, 243, 255), // blue
- Color::from_rgba(3, 169, 244, 255), // lightBlue
- Color::from_rgba(0, 188, 212, 255), // cyan
- Color::from_rgba(0, 150, 136, 255), // teal
- Color::from_rgba(76, 175, 80, 255), // green
- Color::from_rgba(139, 195, 74, 255), // lightGreen
- Color::from_rgba(205, 220, 57, 255), // lime
- Color::from_rgba(255, 235, 59, 255), // yellow
- Color::from_rgba(255, 193, 7, 255), // amber
- Color::from_rgba(255, 152, 0, 255), // orange
- Color::from_rgba(255, 87, 34, 255), // deepOrange
- Color::from_rgba(121, 85, 72, 255), // brown
- Color::from_rgba(158, 158, 158, 255), // grey
- Color::from_rgba(96, 125, 139, 255), // blueGrey
- ];
-
- let hash = Sha256::digest(self.to_string().as_bytes());
-
- let id: usize = hash
- .into_iter()
- .map(|val| val as usize)
- .fold(0, |acc, val| (acc + val) % colors.len());
-
- colors[id]
- }
-}
-
-#[derive(Default, Clone, Copy, Debug)]
-pub struct Color {
- pub red: u32,
- pub green: u32,
- pub blue: u32,
- pub alpha: u32,
-}
-
-impl Color {
- pub fn from_rgba(red: u32, green: u32, blue: u32, alpha: u32) -> Self {
- Self {
- red,
- green,
- blue,
- alpha,
- }
- }
-}
diff --git a/pkgs/by-name/ba/back/src/git_bug/issue/mod.rs b/pkgs/by-name/ba/back/src/git_bug/issue/mod.rs
deleted file mode 100644
index f27bfec..0000000
--- a/pkgs/by-name/ba/back/src/git_bug/issue/mod.rs
+++ /dev/null
@@ -1,185 +0,0 @@
-// Back - An extremely simple git issue tracking system. Inspired by tvix's
-// panettone
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: AGPL-3.0-or-later
-//
-// This file is part of Back.
-//
-// You should have received a copy of the License along with this program.
-// If not, see <https://www.gnu.org/licenses/agpl.txt>.
-
-use std::fmt::Display;
-
-use entity::{Entity, Id};
-use identity::Author;
-use label::Label;
-use operation::Operation;
-use serde_json::Value;
-
-use super::format::{MarkDown, TimeStamp};
-
-pub mod entity;
-pub mod identity;
-pub mod label;
-pub mod operation;
-
-#[derive(Debug, Eq, PartialEq, Copy, Clone)]
-pub enum Status {
- Open,
- Closed,
-}
-impl From<&Value> for Status {
- fn from(value: &Value) -> Self {
- match value.as_u64().expect("This should be a integer") {
- 1 => Self::Open,
- 2 => Self::Closed,
- other => unimplemented!("Invalid status string: '{other}'"),
- }
- }
-}
-impl Display for Status {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- Status::Open => f.write_str("Open"),
- Status::Closed => f.write_str("Closed"),
- }
- }
-}
-
-#[derive(Debug)]
-pub struct CollapsedIssue {
- pub id: Id,
- pub author: Author,
- pub timestamp: TimeStamp,
- pub title: MarkDown,
- pub message: MarkDown,
- pub comments: Vec<Comment>,
- pub status: Status,
- pub last_status_change: TimeStamp,
- pub labels: Vec<Label>,
-}
-impl From<RawCollapsedIssue> for CollapsedIssue {
- fn from(r: RawCollapsedIssue) -> Self {
- macro_rules! get {
- ($name:ident) => {
- r.$name.expect(concat!(
- "'",
- stringify!($name),
- "' is unset, when trying to collapes an issue! (This is likely a bug)"
- ))
- };
- }
-
- Self {
- id: get! {id},
- author: get! {author},
- timestamp: get! {timestamp},
- title: get! {title},
- message: get! {message},
- comments: r.comments,
- status: get! {status},
- last_status_change: get! {last_status_change},
- labels: r.labels,
- }
- }
-}
-
-#[derive(Debug)]
-pub struct Comment {
- pub id: Id,
- pub author: Author,
- pub timestamp: TimeStamp,
- pub message: MarkDown,
-}
-
-#[derive(Debug, Default)]
-pub struct RawCollapsedIssue {
- pub id: Option<Id>,
- pub author: Option<Author>,
- pub timestamp: Option<TimeStamp>,
- pub title: Option<MarkDown>,
- pub message: Option<MarkDown>,
- pub status: Option<Status>,
- pub last_status_change: Option<TimeStamp>,
-
- // NOTE(@bpeetz): These values set here already, because an issue without these
- // would be perfectly valid. <2024-12-26>
- pub labels: Vec<Label>,
- pub comments: Vec<Comment>,
-}
-
-impl RawCollapsedIssue {
- pub fn append_entity(&mut self, entity: Entity) {
- for op in entity.operations {
- match op {
- Operation::AddComment { timestamp, message } => {
- self.comments.push(Comment {
- id: entity.id.clone(),
- author: entity.author.clone(),
- timestamp,
- message,
- });
- }
- Operation::Create {
- timestamp,
- title,
- message,
- } => {
- self.id = Some(entity.id.clone());
- self.author = Some(entity.author.clone());
- self.timestamp = Some(timestamp.clone());
- self.title = Some(title);
- self.message = Some(message);
- self.status = Some(Status::Open); // This is the default in git_bug
- self.last_status_change = Some(timestamp);
- }
- Operation::EditComment {
- timestamp,
- target,
- message,
- } => {
- let comments = &mut self.comments;
-
- let target_comment = comments
- .iter_mut()
- .find(|comment| comment.id == target)
- .expect("The target must be a valid comment");
-
- // TODO(@bpeetz): We should probably set a `edited = true` flag here. <2024-12-26>
- // TODO(@bpeetz): Should we also change the author? <2024-12-26>
-
- target_comment.timestamp = timestamp;
- target_comment.message = message;
- }
- Operation::LabelChange {
- timestamp: _,
- added,
- removed,
- } => {
- let labels = self.labels.clone();
-
- self.labels = labels
- .into_iter()
- .filter(|val| !removed.contains(val))
- .chain(added.into_iter())
- .collect();
- }
- Operation::SetStatus { timestamp, status } => {
- self.status = Some(status);
- self.last_status_change = Some(timestamp);
- }
- Operation::SetTitle {
- timestamp: _,
- title,
- was: _,
- } => {
- self.title = Some(title);
- }
-
- Operation::NoOp {} => unimplemented!(),
- Operation::SetMetadata {} => unimplemented!(),
- }
- }
- }
-}
diff --git a/pkgs/by-name/ba/back/src/git_bug/issue/operation/mod.rs b/pkgs/by-name/ba/back/src/git_bug/issue/operation/mod.rs
deleted file mode 100644
index 7f861a7..0000000
--- a/pkgs/by-name/ba/back/src/git_bug/issue/operation/mod.rs
+++ /dev/null
@@ -1,124 +0,0 @@
-// Back - An extremely simple git issue tracking system. Inspired by tvix's
-// panettone
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: AGPL-3.0-or-later
-//
-// This file is part of Back.
-//
-// You should have received a copy of the License along with this program.
-// If not, see <https://www.gnu.org/licenses/agpl.txt>.
-
-use std::convert::Infallible;
-
-use operation_type::OperationType;
-use serde::Deserialize;
-use serde_json::Value;
-
-use crate::{
- get,
- git_bug::format::{MarkDown, TimeStamp},
-};
-
-use super::{entity, label::Label, Status};
-
-pub mod operation_type;
-
-#[derive(Deserialize, Debug)]
-#[serde(try_from = "Value")]
-pub enum Operation {
- AddComment {
- timestamp: TimeStamp,
- message: MarkDown,
- },
- Create {
- timestamp: TimeStamp,
- title: MarkDown,
- message: MarkDown,
- },
- EditComment {
- timestamp: TimeStamp,
- target: entity::Id,
- message: MarkDown,
- },
- LabelChange {
- timestamp: TimeStamp,
- added: Vec<Label>,
- removed: Vec<Label>,
- },
- SetStatus {
- timestamp: TimeStamp,
- status: Status,
- },
- SetTitle {
- timestamp: TimeStamp,
- title: MarkDown,
- was: MarkDown,
- },
-
- // These seem to be just weird non-operation, operations.
- // defined in: git-bug/entities/bug/operation.go
- NoOp {},
- SetMetadata {},
-}
-
-impl TryFrom<Value> for Operation {
- type Error = Infallible;
-
- fn try_from(value: Value) -> Result<Self, Self::Error> {
- let operation_type = OperationType::from_json_int(
- value
- .get("type")
- .expect("Should exist")
- .as_u64()
- .expect("This should work"),
- );
-
- let op = match operation_type {
- OperationType::AddComment => Self::AddComment {
- timestamp: get! {value, "timestamp" },
- message: get! {value, "message"},
- },
- OperationType::Create => Self::Create {
- timestamp: get! {value, "timestamp"},
- title: get! {value, "title"},
- message: get! {value, "message"},
- },
- OperationType::EditComment => Self::EditComment {
- timestamp: get! {value, "timestamp"},
- target: get! {value, "target"},
- message: get! {value, "message"},
- },
- OperationType::LabelChange => Self::LabelChange {
- timestamp: get! {value, "timestamp"},
- added: serde_json::from_value(
- value
- .get("added")
- .expect("This should be available")
- .to_owned(),
- )
- .expect("This should be parsable"),
- removed: serde_json::from_value(
- value
- .get("removed")
- .expect("This should be available")
- .to_owned(),
- )
- .expect("This should be parsable"),
- },
- OperationType::SetStatus => Self::SetStatus {
- timestamp: get! {value, "timestamp"},
- status: get! {value, "status"},
- },
- OperationType::SetTitle => Self::SetTitle {
- timestamp: get! {value, "timestamp"},
- title: get! {value, "title"},
- was: get! {value, "was"},
- },
- OperationType::NoOp => Self::NoOp {},
- OperationType::SetMetadata => Self::SetMetadata {},
- };
-
- Ok(op)
- }
-}
diff --git a/pkgs/by-name/ba/back/src/git_bug/issue/operation/operation_type.rs b/pkgs/by-name/ba/back/src/git_bug/issue/operation/operation_type.rs
deleted file mode 100644
index 69d272f..0000000
--- a/pkgs/by-name/ba/back/src/git_bug/issue/operation/operation_type.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-// Back - An extremely simple git issue tracking system. Inspired by tvix's
-// panettone
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: AGPL-3.0-or-later
-//
-// This file is part of Back.
-//
-// You should have received a copy of the License along with this program.
-// If not, see <https://www.gnu.org/licenses/agpl.txt>.
-
-pub enum OperationType {
- AddComment,
- Create,
- EditComment,
- LabelChange,
- NoOp,
- SetMetadata,
- SetStatus,
- SetTitle,
-}
-
-impl OperationType {
- // NOTE(@bpeetz): This mapping should always be the same as `git_bug`'s.
- // The mapping is defined in `git-bug/entities/bug/operation.go`. <2024-12-26>
- pub fn to_json_int(self) -> u64 {
- match self {
- OperationType::Create => 1,
- OperationType::SetTitle => 2,
- OperationType::AddComment => 3,
- OperationType::SetStatus => 4,
- OperationType::LabelChange => 5,
- OperationType::EditComment => 6,
- OperationType::NoOp => 7,
- OperationType::SetMetadata => 8,
- }
- }
- pub fn from_json_int(value: u64) -> Self {
- match value {
- 1 => OperationType::Create,
- 2 => OperationType::SetTitle,
- 3 => OperationType::AddComment,
- 4 => OperationType::SetStatus,
- 5 => OperationType::LabelChange,
- 6 => OperationType::EditComment,
- 7 => OperationType::NoOp,
- 8 => OperationType::SetMetadata,
- other => unimplemented!("The operation type {other} is not recognized."),
- }
- }
-}
diff --git a/pkgs/by-name/ba/back/src/git_bug/mod.rs b/pkgs/by-name/ba/back/src/git_bug/mod.rs
deleted file mode 100644
index c0a5372..0000000
--- a/pkgs/by-name/ba/back/src/git_bug/mod.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-// Back - An extremely simple git issue tracking system. Inspired by tvix's
-// panettone
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: AGPL-3.0-or-later
-//
-// This file is part of Back.
-//
-// You should have received a copy of the License along with this program.
-// If not, see <https://www.gnu.org/licenses/agpl.txt>.
-
-pub mod dag;
-pub mod format;
-pub mod issue;
-
-#[macro_export]
-macro_rules! get {
- ($value:expr, $name:expr) => {
- $value
- .get($name)
- .expect(concat!(
- "Expected field ",
- stringify!($name),
- "to exists, but was missing."
- ))
- .into()
- };
-}
diff --git a/pkgs/by-name/ba/back/src/main.rs b/pkgs/by-name/ba/back/src/main.rs
deleted file mode 100644
index 961c39b..0000000
--- a/pkgs/by-name/ba/back/src/main.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-// Back - An extremely simple git issue tracking system. Inspired by tvix's
-// panettone
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: AGPL-3.0-or-later
-//
-// This file is part of Back.
-//
-// You should have received a copy of the License along with this program.
-// If not, see <https://www.gnu.org/licenses/agpl.txt>.
-
-use std::process;
-
-use clap::Parser;
-use config::BackConfig;
-use rocket::routes;
-use web::feed;
-
-use crate::web::{closed, open, show_issue, styles};
-
-mod cli;
-pub mod config;
-mod error;
-pub mod git_bug;
-mod web;
-
-fn main() -> Result<(), String> {
- if let Err(err) = rocket_main() {
- eprintln!("Error {err}");
- process::exit(1);
- } else {
- Ok(())
- }
-}
-
-#[rocket::main]
-async fn rocket_main() -> Result<(), error::Error> {
- let args = cli::Cli::parse();
-
- let config = BackConfig::from_config_file(&args.config_file)?;
-
- rocket::build()
- .mount("/", routes![open, closed, show_issue, styles, feed])
- .manage(config)
- .ignite()
- .await
- .expect("This error should only happen on a miss-configuration.")
- .launch()
- .await?;
-
- Ok(())
-}
diff --git a/pkgs/by-name/ba/back/src/web/issue_html.rs b/pkgs/by-name/ba/back/src/web/issue_html.rs
deleted file mode 100644
index 45c0281..0000000
--- a/pkgs/by-name/ba/back/src/web/issue_html.rs
+++ /dev/null
@@ -1,166 +0,0 @@
-// Back - An extremely simple git issue tracking system. Inspired by tvix's
-// panettone
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: AGPL-3.0-or-later
-//
-// This file is part of Back.
-//
-// You should have received a copy of the License along with this program.
-// If not, see <https://www.gnu.org/licenses/agpl.txt>.
-
-use rocket::response::content::RawHtml;
-
-use crate::{
- config::BackConfig,
- git_bug::{
- format::HtmlString,
- issue::{identity::Author, CollapsedIssue, Comment},
- },
-};
-
-impl CollapsedIssue {
- pub fn to_list_entry(&self) -> RawHtml<String> {
- let comment_list = if self.comments.is_empty() {
- String::new()
- } else {
- let comments_string = if self.comments.len() > 1 {
- "comments"
- } else {
- "comment"
- };
-
- format!(
- r#"
- <span class="comment-count"> - {} {}</span>
- "#,
- self.comments.len(),
- comments_string
- )
- };
-
- let CollapsedIssue {
- id,
- title,
- message: _,
- author,
- timestamp,
- comments: _,
- status: _,
- last_status_change: _,
- labels: _,
- } = self;
-
- let Author { name, email, id: _ } = author;
-
- RawHtml(format!(
- r#"
- <li>
- <a href="/issue/{id}">
- <p>
- <span class="issue-subject">{title}</span>
- </p>
- <span class="issue-number">{id}</span> - <span class="created-by-at">Opened by <span class="user-name">{name}</span> <span class="user-email">&lt;{email}&gt;</span> at <span class="timestamp">{timestamp}</span></span>{comment_list} </a>
- </li>
-"#,
- ))
- }
-
- pub fn to_html(&self, config: &BackConfig) -> RawHtml<String> {
- let comments = if self.comments.is_empty() {
- String::new()
- } else {
- let fmt_comments: String = self
- .comments
- .iter()
- .map(|val| {
- let Comment {
- id,
- author,
- message,
- timestamp,
- } = val;
- let Author {
- name,
- email: _,
- id: _,
- } = author;
-
- format!(
- r#"
- <li class="comment" id="{id}">
- {message}
- <p class="comment-info"><span class="user-name">{name} at {timestamp}</span></p>
- </li>
- "#,
- )
- })
- .collect::<Vec<String>>()
- .join("\n");
-
- format!(
- r#"
- <ol class="issue-history">
- {fmt_comments}
- </ol>
- "#
- )
- };
-
- {
- let CollapsedIssue {
- id,
- title,
- message,
- author,
- timestamp,
- comments: _,
- status: _,
- last_status_change: _,
- labels: _,
- } = self;
- let Author { name, email, id: _ } = author;
- let html_title = HtmlString::from(title.clone());
-
- RawHtml(format!(
- r#"
-<!DOCTYPE html>
-<html lang="en">
- <head>
- <title>{html_title} | Back</title>
- <link href="/style.css" rel="stylesheet" type="text/css">
- <meta content="width=device-width,initial-scale=1" name="viewport">
- </head>
- <body>
- <div class="content">
- <nav>
- <a href="/issues/open">Open Issues</a>
- <a href="/issues/closed">Closed Issues</a>
- </nav>
- <header>
- <h1>{title}</h1>
- <div class="issue-number">{id}</div>
- </header>
- <main>
- <div class="issue-info">
- <span class="created-by-at">Opened by <span class="user-name">{name}</span> <span class="user-email">&lt;{email}&gt;</span> at <span class="timestamp">{timestamp}</span></span>
- </div>
- {message}
- {comments}
- </main>
- <footer>
- <nav>
- <a href="/issues/open">Open Issues</a>
- <a href="{}">Source code</a>
- <a href="/issues/closed">Closed Issues</a>
- </nav>
- </footer>
- </div>
- </body>
-</html>
-"#,
- config.source_code_repository_url
- ))
- }
- }
-}
diff --git a/pkgs/by-name/ba/back/src/web/mod.rs b/pkgs/by-name/ba/back/src/web/mod.rs
deleted file mode 100644
index f7a4077..0000000
--- a/pkgs/by-name/ba/back/src/web/mod.rs
+++ /dev/null
@@ -1,186 +0,0 @@
-// Back - An extremely simple git issue tracking system. Inspired by tvix's
-// panettone
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: AGPL-3.0-or-later
-//
-// This file is part of Back.
-//
-// You should have received a copy of the License along with this program.
-// If not, see <https://www.gnu.org/licenses/agpl.txt>.
-
-use crate::{
- config::BackConfig,
- error::{self, Error},
- git_bug::{
- dag::issues_from_repository,
- issue::{CollapsedIssue, Status},
- },
-};
-use prefix::BackPrefix;
-use rocket::{
- get,
- response::content::{RawCss, RawHtml},
- State,
-};
-
-mod issue_html;
-pub mod prefix;
-
-#[get("/style.css")]
-pub fn styles() -> RawCss<String> {
- RawCss(include_str!("../../assets/style.css").to_owned())
-}
-
-pub fn issue_list_boilerplate(
- config: &State<BackConfig>,
- wanted_status: Status,
- counter_status: Status,
-) -> error::Result<RawHtml<String>> {
- let repository = &config.repository;
-
- let mut issue_list = issues_from_repository(&repository.to_thread_local())?
- .into_iter()
- .map(|issue| issue.collapse())
- .collect::<Vec<CollapsedIssue>>();
-
- // Sort by date descending.
- issue_list.sort_by_key(|issue| unsafe { issue.timestamp.to_unsafe() });
- issue_list.reverse();
-
- let issue_list_str = issue_list.into_iter().fold(String::new(), |acc, issue| {
- format!("{}{}", acc, {
- if issue.status == wanted_status {
- let issue_entry = issue.to_list_entry();
- issue_entry.0
- } else {
- String::new()
- }
- })
- });
-
- let counter_status_lower = counter_status.to_string().to_lowercase();
- Ok(RawHtml(format!(
- r#"
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <title>Back</title>
- <link href="/style.css" rel="stylesheet" type="text/css">
- <meta content="width=device-width,initial-scale=1" name="viewport">
- </head>
- <body>
- <div class="content">
- <header>
- <h1>{wanted_status} Issues</h1>
- </header>
- <main>
- <div class="issue-links">
- <a href="/issues/{counter_status_lower}/">View {counter_status} issues</a>
- <a href="{}">Source code</a>
- <!--
- <form class="issue-search" method="get">
- <input name="search" title="Issue search query" type="search">
- <input class="sr-only" type="submit" value="Search Issues">
- </form>
- -->
- </div>
- <ol class="issue-list">
- {issue_list_str}
- </ol>
- </main>
- </div>
- </body>
- </html>
- "#,
- config.source_code_repository_url
- )))
-}
-
-#[get("/issues/open")]
-pub fn open(config: &State<BackConfig>) -> error::Result<RawHtml<String>> {
- issue_list_boilerplate(config, Status::Open, Status::Closed)
-}
-#[get("/issues/closed")]
-pub fn closed(config: &State<BackConfig>) -> error::Result<RawHtml<String>> {
- issue_list_boilerplate(config, Status::Closed, Status::Open)
-}
-
-#[get("/issues/feed")]
-pub fn feed(config: &State<BackConfig>) -> error::Result<RawHtml<String>> {
- use rss::{ChannelBuilder, Item, ItemBuilder};
-
- //Collect all Items as rss items
- let mut items: Vec<Item> = issues_from_repository(&config.repository.to_thread_local())?
- .into_iter()
- .map(|issue| issue.collapse())
- .map(|issue| {
- ItemBuilder::default()
- .title(issue.title.to_string())
- .author(issue.author.to_string())
- .description(issue.message.to_string())
- .pub_date(issue.timestamp.to_string())
- .link(format!("{}/issue/{}", &config.root.to_string(), issue.id))
- .build()
- })
- .collect();
- //Append all comments after converting them to rss items
- items.extend(
- issues_from_repository(&config.repository.to_thread_local())?
- .into_iter()
- .map(|issue| issue.collapse())
- .filter(|issue| issue.comments.len() > 0)
- .map(|issue| {
- issue
- .comments
- .into_iter()
- .map(|comment| {
- ItemBuilder::default()
- .title(issue.title.to_string())
- .author(comment.author.to_string())
- .description(comment.message.to_string())
- .pub_date(comment.timestamp.to_string())
- .link(format!("{}/issue/{}", &config.root.to_string(), issue.id))
- .build()
- })
- .collect::<Vec<Item>>()
- })
- .flatten()
- .collect::<Vec<Item>>(),
- );
-
- let channel = ChannelBuilder::default()
- .title("Issues")
- .link(config.root.to_string())
- .description(format!("The rss feed for issues on {}.", config.root))
- .items(items)
- .build();
- Ok(RawHtml(channel.to_string()))
-}
-
-#[get("/issue/<prefix>")]
-pub fn show_issue(
- config: &State<BackConfig>,
- prefix: Result<BackPrefix, gix::hash::prefix::from_hex::Error>,
-) -> error::Result<RawHtml<String>> {
- // NOTE(@bpeetz): Explicitly unwrap the `prefix` here (instead of taking the unwrapped value as
- // argument), to avoid triggering rockets "errors forward to the next route" feature.
- // This ensures, that our error message actually reaches the user. <2024-12-26>
- let prefix = prefix?;
-
- let repository = config.repository.to_thread_local();
-
- let all_issues: Vec<CollapsedIssue> = issues_from_repository(&repository)?
- .into_iter()
- .map(|val| val.collapse())
- .collect();
-
- let maybe_issue = all_issues
- .iter()
- .find(|issue| issue.id.to_string().starts_with(&prefix.to_string()));
-
- match maybe_issue {
- Some(issue) => Ok(issue.to_html(config)),
- None => Err(Error::IssuesPrefixMissing { prefix }),
- }
-}
diff --git a/pkgs/by-name/ba/back/src/web/prefix.rs b/pkgs/by-name/ba/back/src/web/prefix.rs
deleted file mode 100644
index 5143799..0000000
--- a/pkgs/by-name/ba/back/src/web/prefix.rs
+++ /dev/null
@@ -1,35 +0,0 @@
-// Back - An extremely simple git issue tracking system. Inspired by tvix's
-// panettone
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: AGPL-3.0-or-later
-//
-// This file is part of Back.
-//
-// You should have received a copy of the License along with this program.
-// If not, see <https://www.gnu.org/licenses/agpl.txt>.
-
-use std::fmt::Display;
-
-use gix::hash::Prefix;
-use rocket::request::FromParam;
-
-#[derive(Debug)]
-pub struct BackPrefix {
- prefix: Prefix,
-}
-impl Display for BackPrefix {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- self.prefix.fmt(f)
- }
-}
-
-impl<'a> FromParam<'a> for BackPrefix {
- type Error = gix::hash::prefix::from_hex::Error;
-
- fn from_param(param: &'a str) -> Result<Self, Self::Error> {
- let prefix = Prefix::from_hex(param)?;
-
- Ok(Self { prefix })
- }
-}