From 4d33e4fe4342db457f1e299ab484acfd8f0e0050 Mon Sep 17 00:00:00 2001 From: emma Date: Mon, 27 Apr 2026 18:26:37 -0600 Subject: [PATCH] git.rs, render.rs, test_render.rs: updates rendering code and exposes rendering API --- src/backend/git.rs | 141 +++++++++++++++++++++++------------------ src/backend/render.rs | 79 ++++++++++++++--------- src/bin/test_render.rs | 77 +++++----------------- 3 files changed, 143 insertions(+), 154 deletions(-) diff --git a/src/backend/git.rs b/src/backend/git.rs index 61e10a5..958b44c 100644 --- a/src/backend/git.rs +++ b/src/backend/git.rs @@ -21,9 +21,8 @@ use std::error::Error; use gix::{ - Repository, + Object, Tree, - object::tree::EntryKind, revision::walk::Sorting, }; use gix_traverse::commit::simple::CommitTimeOrder; @@ -45,12 +44,13 @@ fn get_entries(tree: Tree) -> Result, Box> { let order = Sorting::ByCommitTime(CommitTimeOrder::NewestFirst); for e in tree.iter() { - let entry = e.unwrap(); + let entry = e?; let mut entry_t = Entry::default(); entry_t.path = entry.filename().to_string(); + use gix::object::tree::EntryKind::*; entry_t.class = match entry.kind() { - EntryKind::Tree | EntryKind::Commit => "directory", + Tree | Commit => "directory", _ => "file", }.to_owned(); @@ -60,7 +60,9 @@ fn get_entries(tree: Tree) -> Result, Box> { if commit.tree()?.lookup_entry([path_slice])?.is_some() { entry_t.last_commit = commit.id.to_string(); - entry_t.last_commit_message = commit.message_raw()?.to_string(); + entry_t.last_commit_message = commit + .message_raw()? + .to_string(); entry_t.last_commit_time = format_iso8601_utc( commit.time()?.seconds )?; @@ -75,61 +77,76 @@ fn get_entries(tree: Tree) -> Result, Box> { Ok(entries) } -pub fn repo_to_context(r: Repository) -> Result> { - let branches = r.clone().branch_names() - .iter() - .map(|x| x.to_owned().to_owned()) - .collect::>(); - - /* @ is HEAD */ - 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().shorten().to_string(); - - let entries = get_entries(tree)?; - - /* - let mut readme_content = String::new(); - - /* TODO: determine what file to use as README better */ - let readme_candidates = entries.iter().filter(|e| { - match e.path.as_str().to_uppercase().as_str() { - "README" => true, - "README.MD" => true, - _ => false, - } - }); - - if let Some(readme) = readme_candidates.get(0) { - /* unwrapping is ok because readme exists in Tree */ - readme_content = tree.find_entry(readme).unwrap(); - - todo!("convert Entry into BlobRef to get data") - } - */ - - let dir = r.common_dir(); - let name = dir.file_name().unwrap().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("directory", ""); - - ctx.insert("readme_content", "test readme"); - - ctx.insert("branches", &branches); - ctx.insert("entries", &entries); - ctx.insert("owner", "anon"); - ctx.insert("branch", ¤t_branch); - ctx.insert("repo", name); - ctx.insert("entries", &entries); - - Ok(ctx) +pub trait ToContext { + type Error; + fn to_context(&self) -> Result; +} + +impl ToContext for Object<'_> { + type Error = Box; + + fn to_context(&self) -> Result { + let mut ctx = Context::new(); + let current_branch = self.repo + .head()? + .referent_name() + .unwrap() + .shorten() + .to_string(); + + use gix::object::Kind; + match self.kind { + Kind::Blob => todo!("need sasha to make a file page"), + Kind::Tag => todo!("idk what this needs to look like"), + Kind::Commit => todo!("need sasha to make a commit page"), + Kind::Tree => { + let tree = self.clone().peel_to_tree()?; + let entries = get_entries(tree)?; + let repo = self.repo; + let dir = repo.common_dir(); + let name = dir.file_name().unwrap().to_str().unwrap(); + let branches = repo.clone().branch_names() + .iter() + .map(|x| x.to_owned().to_owned()) + .collect::>(); + + /* + let mut readme_content = String::new(); + + /* TODO: determine what file to use as README better */ + let readme_candidates = entries.iter().filter(|e| { + match e.path.as_str().to_uppercase().as_str() { + "README" => true, + "README.MD" => true, + _ => false, + } + }); + + if let Some(readme) = readme_candidates.get(0) { + /* unwrapping is ok because readme exists in Tree */ + readme_content = tree.find_entry(readme).unwrap(); + + todo!("convert Entry into BlobRef to get data") + } + */ + + /* 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("directory", ""); + + ctx.insert("readme_content", "test readme"); + + ctx.insert("branches", &branches); + ctx.insert("entries", &entries); + ctx.insert("owner", "anon"); + ctx.insert("branch", ¤t_branch); + ctx.insert("repo", name); + ctx.insert("entries", &entries); + }, + }; + Ok(ctx) + } } diff --git a/src/backend/render.rs b/src/backend/render.rs index e0ec37f..9e20bc7 100644 --- a/src/backend/render.rs +++ b/src/backend/render.rs @@ -21,27 +21,27 @@ use std::{ error::Error, fmt::{ self, Display, Formatter }, - path::Path, + io::Write, }; -use tera::{ Context, Tera }; +use gix::open; +use tera::Tera; -/* TODO: silt needs to export the HTTP error types */ -#[non_exhaustive] -pub enum PageError { - NotFound, -} +use crate::backend::git::ToContext; #[non_exhaustive] +#[derive(Debug, Clone)] pub enum PageKind { Code, Dashboard, - Tickets, Project, - RawCode, Repo, + RepoSubDir, + Settings, + Tickets, User, - Invalid(PageError), + /* TODO: silt exports a generic frontend non-success status trait */ + Invalid, } impl Display for PageKind { @@ -51,37 +51,56 @@ impl Display for PageKind { let path = match self { Code => "repo/code.html", Dashboard => "dashboard.html", + Project => "project.html", + Repo => "repo/repo.html", + RepoSubDir => "repo/repo.html", + Settings => "user/settings.html", Tickets => "repo/tickets.html", - Repo => "repo/code.html", User => "user.html", - _ => "", + Invalid => todo!("generate error pages based on error type"), }; - write!(f, "./assets/templates/{}", path) + write!(f, "{}", path) } } +#[derive(Debug, Clone)] pub struct Page { - kind: PageKind, - path: String, - branch: Option, - commit: Option, - tag: Option, + pub kind: PageKind, + pub path: String, + pub branch: Option, + pub commit: Option, + pub tag: Option, + pub user: Option, } -/* impl Page { - pub fn render(&self) -> Result> { - let template_dir = self.kind.to_string(); - let template = String::from_utf8(Path::new(&template_dir) - .to_path_buf() - .as_mut_os_string() - .as_encoded_bytes() - .to_vec() - )?; + pub fn render( + &self, mut dest: Box + ) -> Result<(), Box> { + /* TODO: replace ./assets/ with the actual templates directory, + * i.e. /usr/local/share/mintee/ */ + let tera = Tera::new("./assets/templates/**/*")?; - let tera = Tera::new(&page_dir)?; - Ok(tera.render(&template, &ctx)?) + use PageKind::*; + let ctx = match self.kind { + Code => todo!(), + Dashboard | Project | Settings | Tickets | User | Invalid => { + todo!() + }, + Repo => { + open(&self.path)? + /*.rev_parse_single("@")?*/ + .head_tree()? + .id() + .object()? + .to_context()? + }, + RepoSubDir => todo!(), + }; + + Ok(dest.write_all( + tera.render(&self.kind.to_string(), &ctx)?.as_bytes() + )?) } } -*/ diff --git a/src/bin/test_render.rs b/src/bin/test_render.rs index 66641df..ed0ee6b 100644 --- a/src/bin/test_render.rs +++ b/src/bin/test_render.rs @@ -19,73 +19,26 @@ */ use std::{ - env::{ args, current_dir }, + env::current_dir, error::Error, + io::stdout, }; -use gix::open; -use tera::Tera; - -use mintee::backend::git::repo_to_context; - -/* -impl From 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 - } -} -*/ +use mintee::backend::render::{ Page, PageKind }; fn main() -> Result<(), Box> { - if let Some(templates) = args().collect::>().get(1) { - let tera = Tera::new(templates.as_str())?; - let ctx = repo_to_context(open(current_dir()?)?)?; + let path = String::from_utf8( + current_dir()?.into_os_string().into_encoded_bytes() + )?; - println!("{}", tera.render("repo.html", &ctx)?); + let page = Page { + kind: PageKind::Repo, + path, + branch: None, + commit: None, + tag: None, + user: None, + }; - Ok(()) - } else { - eprintln!("Usage: {} template_glob", args().collect::>()[0]); - std::process::exit(64); - } + page.render(Box::new(stdout())) }