reorganize code & switch to gix

This commit is contained in:
2026-03-06 18:05:57 -07:00
parent 331387a56a
commit cf3f8b0aea
13 changed files with 1788 additions and 559 deletions

62
src/bin/backend/git.rs Normal file
View File

@@ -0,0 +1,62 @@
use std::error::Error;
use gix::{ Repository, traverse::tree::Recorder, prelude::* };
use serde::Serialize;
use tera::Context;
#[derive(Serialize)]
struct Entry {
mode: String,
hash: String,
path: String,
}
pub fn repo_to_context(r: Repository) -> Result<Context, Box<dyn Error>> {
let mut entries = Vec::new();
let branches = r.clone().branch_names()
.iter()
.map(|x| x.to_owned().to_owned())
.collect::<Vec<_>>();
let mut rec = Recorder::default();
let tree = r.rev_parse_single("@")?.object()?.peel_to_tree()?;
/* replace with configurable branch name when we have a database */
let current_branch = r.head()?.referent_name().unwrap().to_string();
tree.traverse().breadthfirst(&mut rec)?;
for e in rec.records.iter() {
entries.push(Entry {
mode: e.mode.value().to_string(),
hash: e.oid.to_string(),
path: e.filepath.to_string(),
});
}
let dir = r.git_dir();
let name = dir.file_name().unwrap().to_str().unwrap();
let directory = dir.as_os_str().to_str().unwrap();
let mut ctx = Context::new();
/* stubs til we have a real database */
ctx.insert("user", "anon");
ctx.insert("site", "TiB.");
ctx.insert("notif_count", "");
ctx.insert("ticket_count", "(47)");
ctx.insert("readme_content", "test readme");
ctx.insert("branches", &branches);
ctx.insert("entries", &entries);
ctx.insert("owner", "anon");
ctx.insert("branch", &current_branch);
ctx.insert("repo", name);
ctx.insert("directory", directory);
ctx.insert("entries", &entries);
Ok(ctx)
}

65
src/bin/backend/render.rs Normal file
View File

@@ -0,0 +1,65 @@
/*
* Copyright (c) 2025 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This file is part of Mintee.
*
* Mintee is free software: you can redistribute it and/or modify it under the
* terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* Mintee is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Mintee. If not, see https://www.gnu.org/licenses/.
*/
use std::{
error::Error,
fmt::{ self, Display, Formatter },
path::Path,
};
use tera::{ Context, Tera };
#[non_exhaustive]
pub enum PageKind {
Code,
Dashboard,
Tickets,
User,
}
impl PageKind {
pub fn render_page(&self, ctx: Context) -> Result<String, Box<dyn Error>> {
let page_dir = self.to_string();
let template = String::from_utf8(Path::new(&page_dir)
.to_path_buf()
.as_mut_os_string()
.as_encoded_bytes()
.to_vec()
)?;
let tera = Tera::new(&page_dir)?;
Ok(tera.render(&template, &ctx)?)
}
}
impl Display for PageKind {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
use PageKind::*;
let path = match self {
Code => "repo/code.html",
Dashboard => "dashboard.html",
Tickets => "repo/tickets.html",
User => "user.html",
};
write!(f, "./assets/templates/{}", path)
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (c) 20252026 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This file is part of Mintee.
*
* Mintee is free software: you can redistribute it and/or modify it under the
* terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* Mintee is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Mintee. If not, see https://www.gnu.org/licenses/.
*/
use std::{
env::{ args, current_dir },
error::Error,
};
use gix::open;
use tera::{ Context, Tera };
mod git;
use git::repo_to_context;
/*
impl From<GitRepo> for Context {
fn from(repo: GitRepo) -> Self {
let mut ctx = Context::new();
let directory = format!("{}/{}", repo.owner, repo.name);
let main_branch = repo.branches
.iter()
.find(|b| b.name == "main")
.unwrap();
let latest_commit = &main_branch.commits[0];
let hash = latest_commit.hash.clone();
let mut entries = Vec::new();
for e in latest_commit.entries.iter() {
let entry = SampleEntry {
class: e.kind.to_string(),
last_commit: hash.clone(),
last_commit_message: latest_commit.message.clone().unwrap(),
last_commit_time: latest_commit.time,
path: e.path.clone(),
};
entries.push(entry);
}
/* stubs til we have a real database */
ctx.insert("user", "anon");
ctx.insert("site", "TiB.");
ctx.insert("notif_count", "");
ctx.insert("ticket_count", "(47)");
ctx.insert("readme_content", "this is a readme");
ctx.insert("owner", &repo.owner);
ctx.insert("branch", &main_branch.name);
ctx.insert("repo", &repo.name);
ctx.insert("directory", &directory);
ctx.insert("entries", &entries);
ctx
}
}
*/
fn main() -> Result<(), Box<dyn Error>> {
if let Some(templates) = args().collect::<Vec<_>>().get(1) {
let tera = Tera::new(templates.as_str())?;
let ctx = repo_to_context(open(current_dir()?)?)?;
println!("{}", tera.render("code.html", &ctx)?);
Ok(())
} else {
eprintln!("Usage: {} template_glob", args().collect::<Vec<_>>()[0]);
std::process::exit(64);
}
}

25
src/bin/backend/test.rs Normal file
View File

@@ -0,0 +1,25 @@
use std::{ env::current_dir, error::Error };
use gix::{ traverse::tree::Recorder, prelude::* };
fn main() -> Result<(), Box<dyn Error>> {
let r = gix::open(current_dir()?)?;
let mut rec = Recorder::default();
let tree = r.rev_parse_single("@")?.object()?.peel_to_tree()?;
tree.traverse().breadthfirst(&mut rec)?;
for e in rec.records.iter() {
println!("{}", e.filepath)
}
/*
for e in tree.iter() {
println!("{}", e?.filename())
}
*/
Ok(())
}

91
src/bin/test_render.rs Normal file
View File

@@ -0,0 +1,91 @@
/*
* Copyright (c) 20252026 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This file is part of Mintee.
*
* Mintee is free software: you can redistribute it and/or modify it under the
* terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* Mintee is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Mintee. If not, see https://www.gnu.org/licenses/.
*/
use std::{
env::{ args, current_dir },
error::Error,
};
use gix::open;
use tera::{ Context, Tera };
use mintee::backend::git::repo_to_context;
/*
impl From<GitRepo> for Context {
fn from(repo: GitRepo) -> Self {
let mut ctx = Context::new();
let directory = format!("{}/{}", repo.owner, repo.name);
let main_branch = repo.branches
.iter()
.find(|b| b.name == "main")
.unwrap();
let latest_commit = &main_branch.commits[0];
let hash = latest_commit.hash.clone();
let mut entries = Vec::new();
for e in latest_commit.entries.iter() {
let entry = SampleEntry {
class: e.kind.to_string(),
last_commit: hash.clone(),
last_commit_message: latest_commit.message.clone().unwrap(),
last_commit_time: latest_commit.time,
path: e.path.clone(),
};
entries.push(entry);
}
/* stubs til we have a real database */
ctx.insert("user", "anon");
ctx.insert("site", "TiB.");
ctx.insert("notif_count", "");
ctx.insert("ticket_count", "(47)");
ctx.insert("readme_content", "this is a readme");
ctx.insert("owner", &repo.owner);
ctx.insert("branch", &main_branch.name);
ctx.insert("repo", &repo.name);
ctx.insert("directory", &directory);
ctx.insert("entries", &entries);
ctx
}
}
*/
fn main() -> Result<(), Box<dyn Error>> {
if let Some(templates) = args().collect::<Vec<_>>().get(1) {
let tera = Tera::new(templates.as_str())?;
let ctx = repo_to_context(open(current_dir()?)?)?;
println!("{}", tera.render("code.html", &ctx)?);
Ok(())
} else {
eprintln!("Usage: {} template_glob", args().collect::<Vec<_>>()[0]);
std::process::exit(64);
}
}