diff --git a/.gitignore b/.gitignore index ea8c4bf..cab8263 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /target +/assets/output.html +/assets/index2.html diff --git a/Cargo.lock b/Cargo.lock index 9a516d2..d6d3126 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,9 +65,9 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "cc" -version = "1.2.31" +version = "1.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" +checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" dependencies = [ "jobserver", "libc", @@ -243,8 +243,6 @@ dependencies = [ "libc", "libgit2-sys", "log", - "openssl-probe", - "openssl-sys", "url", ] @@ -480,9 +478,7 @@ checksum = "1c42fe03df2bd3c53a3a9c7317ad91d80c81cd1fb0caec8d7cc4cd2bfa10c222" dependencies = [ "cc", "libc", - "libssh2-sys", "libz-sys", - "openssl-sys", "pkg-config", ] @@ -492,20 +488,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" -[[package]] -name = "libssh2-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "220e4f05ad4a218192533b300327f5150e809b54c4ec83b5a1d91833601811b9" -dependencies = [ - "cc", - "libc", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", -] - [[package]] name = "libz-sys" version = "1.1.22" @@ -561,24 +543,6 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" -[[package]] -name = "openssl-probe" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - -[[package]] -name = "openssl-sys" -version = "0.9.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "parse-zoneinfo" version = "0.3.1" @@ -785,9 +749,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" diff --git a/Cargo.toml b/Cargo.toml index 8db684b..8414fec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,8 +6,12 @@ edition = "2024" [[bin]] name = "frontend" +[[bin]] +name = "test_render" +path = "src/test-render.rs" + [dependencies] -git2 = "0.20.2" +git2 = { version = "0.20.2", default-features = false } serde = { version = "1.0.219", default-features = false, features = ["derive"] } #inkjet = "0.11.1" #markdown = "1.0.0" diff --git a/assets/index.html b/assets/index.html index 7424ab4..3ce5b21 100644 --- a/assets/index.html +++ b/assets/index.html @@ -26,7 +26,7 @@ mintea - + + + + +
+ +
+ +
+ + viewing + + owner/repo + + + +
+ +
+ + +
+
+

Ticket title

+

Ratione quis totam tempora sit magnam voluptas. Recusandae qui illo nulla eligendi. Perspiciatis iusto numquam suscipit aspernatur quibusdam ex.

+

Consequuntur atque rerum culpa numquam et et possimus dolor. Quidem possimus quia et consectetur. Debitis autem cupiditate vero maxime et libero quae. Tenetur vero architecto iure dolores numquam assumenda enim.

+

Dolorem facere animi quis repudiandae rerum dolor. Reprehenderit consequatur quae quia quos illum. Labore ducimus ut quaerat et corrupti cupiditate tenetur.

+

Nisi ut aut sunt dignissimos. Alias magnam itaque deleniti alias quibusdam id possimus eos. Reiciendis et eos placeat. Vero eligendi occaecati quaerat vitae voluptatem deserunt.

+

Dolor aut corrupti et officia id. Minus ipsam assumenda fugiat neque. Neque et saepe maiores iusto maiores. Dolor et at reprehenderit exercitationem totam neque.

+

Heading

+

Ratione quis totam tempora sit magnam voluptas. Recusandae qui illo nulla eligendi. Perspiciatis iusto numquam suscipit aspernatur quibusdam ex.

+

Consequuntur atque rerum culpa numquam et et possimus dolor. Quidem possimus quia et consectetur. Debitis autem cupiditate vero maxime et libero quae. Tenetur vero architecto iure dolores numquam assumenda enim.

+

Dolorem facere animi quis repudiandae rerum dolor. Reprehenderit consequatur quae quia quos illum. Labore ducimus ut quaerat et corrupti cupiditate tenetur.

+

Nisi ut aut sunt dignissimos. Alias magnam itaque deleniti alias quibusdam id possimus eos. Reiciendis et eos placeat. Vero eligendi occaecati quaerat vitae voluptatem deserunt.

+

Dolor aut corrupti et officia id. Minus ipsam assumenda fugiat neque. Neque et saepe maiores iusto maiores. Dolor et at reprehenderit exercitationem totam neque.

+

Heading

+

Ratione quis totam tempora sit magnam voluptas. Recusandae qui illo nulla eligendi. Perspiciatis iusto numquam suscipit aspernatur quibusdam ex.

+

Consequuntur atque rerum culpa numquam et et possimus dolor. Quidem possimus quia et consectetur. Debitis autem cupiditate vero maxime et libero quae. Tenetur vero architecto iure dolores numquam assumenda enim.

+

Dolorem facere animi quis repudiandae rerum dolor. Reprehenderit consequatur quae quia quos illum. Labore ducimus ut quaerat et corrupti cupiditate tenetur.

+

Nisi ut aut sunt dignissimos. Alias magnam itaque deleniti alias quibusdam id possimus eos. Reiciendis et eos placeat. Vero eligendi occaecati quaerat vitae voluptatem deserunt.

+

Dolor aut corrupti et officia id. Minus ipsam assumenda fugiat neque. Neque et saepe maiores iusto maiores. Dolor et at reprehenderit exercitationem totam neque.

+
+
+
+ + + + + + + diff --git a/src/backend.rs b/src/backend.rs deleted file mode 100644 index 80153e9..0000000 --- a/src/backend.rs +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (c) 2025 Emma Tebibyte - * 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/. - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * MIT License - * - * Copyright (c) 2021 Sergey "Shnatsel" Davidoff - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -use std::{ - error::Error, - path::{ PathBuf, Path }, -}; - -use git2::{ Commit, Repository, Sort }; -use serde::Serialize; - -#[derive(Serialize)] -pub struct GitCommit { - author: (Option, Option), // name, e-mail - hash: String, - message: Option, - short_hash: Option, - time: i64, // seconds since Unix epoch -} - -impl TryFrom> for GitCommit { - type Error = git2::Error; - - fn try_from(commit: Commit) -> Result { - let hash = commit.id().to_string(); - let short_hash = commit.as_object().short_id()?.as_str().map(|f| f.to_owned()); - let message = commit.message().map(|f| f.to_owned()); - let commit_signature = commit.author(); - let name = commit_signature.name().map(|f| f.to_owned()); - let address = commit_signature.email().map(|f| f.to_owned()); - let author = (name, address); - let time = commit.time().seconds(); - - Ok(GitCommit { author, hash, message, short_hash, time }) - } -} - -#[derive(Serialize)] -pub struct GitEntry { - last_commit: GitCommit, - path: String, -} - -impl GitEntry { - fn new(commit: GitCommit, path: String) -> Self { - GitEntry { last_commit: commit, path } - } -} - - -#[derive(Serialize)] -pub struct GitRepo { - branch: Option, - entries: Vec, - last_commit: GitCommit, - name: String, - owner: String, -} - -impl GitRepo { - fn open(path: PathBuf) -> Result> { - let repo = Repository::open(path.clone())?; - let entries = Self::get_entries(&repo)?; - - let full_path = path.clone().as_path().canonicalize()?; - - let relative_path = full_path.strip_prefix("/var/mintee/repos")?; - - let owner = String::from_utf8( - relative_path - .parent() - .unwrap() - .to_path_buf() - .as_mut_os_string() - .as_encoded_bytes() - .to_vec() - )?; - - let name = String::from_utf8( - relative_path - .file_name() - .unwrap() - .to_owned() - .as_encoded_bytes() - .to_vec() - )?; - - let head = repo.head()?; - let branch = head.shorthand().map(|f| f.to_owned()); - - let last_commit = GitCommit::try_from(head.peel_to_commit()?)?; - - Ok(GitRepo { branch, entries, last_commit, name, owner }) - } - - fn get_remote_repo() { // for AP repos - todo!(); - } - - fn get_entries(repo: &Repository) -> Result, Box> { - let mut entries = Vec::new(); - let mut revwalk = repo.revwalk()?; - - revwalk.set_sorting(Sort::TOPOLOGICAL | Sort::TIME)?; - revwalk.push_head()?; - - for commit_id in revwalk { - let commit_id = commit_id?; - let commit = repo.find_commit(commit_id)?; - if commit.parent_count() <= 1 { - let tree = commit.tree()?; - let prev_tree = match commit.parent_count() { - 1 => Some(commit.parent(0)?.tree()?), - 0 => None, - _ => unreachable!(), - }; - - let diff = repo - .diff_tree_to_tree(prev_tree.as_ref(), Some(&tree), None)?; - - for delta in diff.deltas() { - if let Some(file_path) = delta.new_file().path() { - let p = String::from_utf8(file_path - .to_path_buf() - .as_mut_os_string() - .as_encoded_bytes() - .to_vec() - )?; - let c = GitCommit::try_from(commit.clone())?; - entries.push(GitEntry::new(c, p)); - } - } - } - } - Ok(entries) - } -} diff --git a/src/git.rs b/src/git.rs new file mode 100644 index 0000000..88d9729 --- /dev/null +++ b/src/git.rs @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2025 Emma Tebibyte + * 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/. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * MIT License + * + * Copyright (c) 2021 Sergey "Shnatsel" Davidoff + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +use std::{ + error::Error, + path::PathBuf, +}; + +use git2::{ Branch, Commit, FileMode, Repository, Sort, TreeEntry }; +use tera::Context; + +/* TODO: implement From for Context where T: all Git* types */ + +pub enum GitEntryKind { + Directory, + File, + Link, +} + +impl ToString for GitEntryKind { + fn to_string(&self) -> String { + match self { + GitEntryKind::Directory => "directory".to_owned(), + GitEntryKind::File => "file".to_owned(), + GitEntryKind::Link => "link".to_owned(), + } + } +} + +pub struct GitEntry { + pub entries: Vec, + pub kind: GitEntryKind, + pub path: String, +} + +impl GitEntry { + fn new(entries: Vec, kind: GitEntryKind, path: String) -> Self { + GitEntry { entries, kind, path } + } +} + +impl TryFrom> for GitEntry { + type Error = std::string::FromUtf8Error; + + fn try_from(entry: TreeEntry) -> Result { + let entries = Vec::new(); + let path = String::from_utf8(entry.name_bytes().to_vec())?; + let kind = match entry.filemode() { + m if m == ::from(FileMode::Blob) => GitEntryKind::File, + m if m == ::from(FileMode::BlobExecutable) => GitEntryKind::File, + m if m == ::from(FileMode::BlobGroupWritable) => unreachable!(), + m if m == ::from(FileMode::Link) => GitEntryKind::File, + m if m == ::from(FileMode::Tree) => GitEntryKind::Directory, + m if m == ::from(FileMode::Commit) => GitEntryKind::File, + m if m == ::from(FileMode::Unreadable) => todo!(), + _ => GitEntryKind::Directory, + }; + + Ok(Self::new(entries, kind, path)) + } +} + +pub struct GitCommit { + pub author: (Option, Option), // name, e-mail + pub entries: Vec, + pub hash: String, + pub message: Option, + pub short_hash: Option, + pub time: i64, // seconds since Unix epoch +} + +impl From for Context { + fn from(value: GitCommit) -> Self { + let mut ctx = Context::new(); + + todo!("proc_macro for populating"); + + ctx + } +} + +impl TryFrom> for GitCommit { + type Error = Box; + + fn try_from(commit: Commit) -> Result { + let hash = commit.id().to_string(); + let short_hash = commit.as_object().short_id()?.as_str().map(|f| f.to_owned()); + let message = commit.message().map(|f| f.to_owned()); + let commit_signature = commit.author(); + let name = commit_signature.name().map(|f| f.to_owned()); + let address = commit_signature.email().map(|f| f.to_owned()); + let author = (name, address); + let time = commit.time().seconds(); + let entries = commit + .tree()? + .iter() + .map(|c| -> Result { + Ok(GitEntry::try_from(c)?) + }).collect::, _>>()?; + + Ok(GitCommit { author, entries, hash, message, short_hash, time }) + } +} + +pub struct GitBranch { + pub commits: Vec, + pub name: String, +} + +struct GitBranchWrapper<'a> { + branch: Branch<'a>, + repo: &'a Repository, +} + +impl<'a> GitBranchWrapper<'a> { + fn new(branch: Branch<'a>, repo: &'a Repository) -> Self { + Self { branch, repo } + } +} + +impl TryFrom> for GitBranch { + type Error = Box; + + fn try_from(branch: GitBranchWrapper) -> Result { + let name = String::from_utf8(branch.branch.name_bytes()?.to_vec())?; + let repo = branch.repo; + let branch_oid = branch.branch.get().resolve()?.target().unwrap(); + + let mut revwalk = repo.revwalk()?; + revwalk.set_sorting(Sort::TOPOLOGICAL | Sort::TIME)?; + revwalk.push(branch_oid)?; + + let commits = revwalk + .into_iter() + .map(|o| -> Result> { + Ok(GitCommit::try_from(repo.find_commit(o?)?)?) + }).collect::, _>>()?; + + Ok(GitBranch { commits, name }) + } + +} + +pub struct GitRepo { + pub branches: Vec, + pub name: String, + pub owner: String, +} + +impl GitRepo { + pub fn open( + path: PathBuf, prefix: Option + ) -> Result> { + let repo = Repository::open(path.clone())?; + + let branches = repo + .branches(None)? + .map(|b| -> Result> { + Ok(GitBranch::try_from(GitBranchWrapper::new(b?.0, &repo))?) + }).collect::, _>>()?; + /* + let mut revwalk = repo.revwalk()?; + + revwalk.set_sorting(Sort::TOPOLOGICAL | Sort::TIME)?; + revwalk.push_head()?; + let commits = revwalk + .into_iter() + .map(|o| -> Result> { + Ok(GitCommit::try_from(repo.find_commit(o?)?)?) + }).collect::, _>>()?; + */ + + let full_path = path.clone().as_path().canonicalize()?; + let relative_path = match prefix { + Some(p) => full_path.strip_prefix(p)?.canonicalize()?, + None => full_path, + }; + + let owner = String::from_utf8( + relative_path + .parent() + .unwrap() + .to_path_buf() + .as_mut_os_string() + .as_encoded_bytes() + .to_vec() + )?; + + let name = String::from_utf8( + relative_path + .file_name() + .unwrap() + .to_owned() + .as_encoded_bytes() + .to_vec() + )?; + + Ok(GitRepo { branches, name, owner }) + } + + pub fn get_remote_repo() { // for AP repos + todo!(); + } +} diff --git a/src/render.rs b/src/render.rs index a549667..58bba25 100644 --- a/src/render.rs +++ b/src/render.rs @@ -16,56 +16,50 @@ * * You should have received a copy of the GNU Affero General Public License * along with Mintee. If not, see https://www.gnu.org/licenses/. - * - * This file incorporates work covered by the following copyright and - * permission notice: */ -use std::path::PathBuf; +use std::{ + error::Error, + fmt::{ self, Display, Formatter }, + path::Path, +}; -use git2::Repository; use tera::{ Context, Tera }; -trait ToContext { - fn to_context(self) -> Result; +#[non_exhaustive] +pub enum PageKind { + Code, + Dashboard, + Tickets, + User, } -// TODO: move this data into backend.rs -impl ToContext for Repository { - fn to_context(self) -> Result { - let repo = self.commondir(); - let _index = self.index()?; - let head = self.head()?; - let branch = if head.is_branch() { - head.shorthand().unwrap() - } else { "detached" }; - let head_commit = head.peel_to_commit()?; - let entries = get_entries(&self)?; - let committer = head_commit.committer().name().unwrap().to_owned(); - let _author = head_commit.author().name().unwrap().to_owned(); +impl PageKind { + pub fn render_page(&self, ctx: Context) -> Result> { + 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 mut ctx = Context::new(); - - // stub until we have database - ctx.insert("user", "anon"); - ctx.insert("site", "TiB."); - ctx.insert("notif_count", ""); - ctx.insert("ticket_count", "(47)"); - ctx.insert("owner", &committer); - ctx.insert("repo", repo); - ctx.insert("branch", &branch); - ctx.insert("directory", repo); - ctx.insert("entries", &entries); - ctx.insert("readme_content", "this is a readme"); - - Ok(ctx) + let tera = Tera::new(&page_dir)?; + Ok(tera.render(&template, &ctx)?) } } -fn render_path(path: PathBuf) -> tera::Result { - let tera = Tera::new("./assets/templates/repo/*")?; - let repo = Repository::discover(path).unwrap(); - let context = repo.to_context().unwrap(); +impl Display for PageKind { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + use PageKind::*; - tera.render("code.html", &context) + let path = match self { + Code => "repo/code.html", + Dashboard => "dashboard.html", + Tickets => "repo/tickets.html", + User => "user.html", + }; + + write!(f, "./assets/templates/{}", path) + } } diff --git a/src/test-render.rs b/src/test-render.rs new file mode 100644 index 0000000..6332b45 --- /dev/null +++ b/src/test-render.rs @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2025 Emma Tebibyte + * 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 serde::Serialize; +use tera::{ Context, Tera }; + +#[path="git.rs"] +mod git; + +use git::GitRepo; + +#[derive(Serialize)] +struct SampleEntry { + class: String, + last_commit_message: String, + last_commit: String, + last_commit_time: i64, + path: String, +} + +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 + } +} + +fn main() -> Result<(), Box> { + if let Some(templates) = args().collect::>().get(1) { + let tera = Tera::new(templates.as_str())?; + let ctx = Context::from( + GitRepo::open(current_dir()?.to_path_buf(), None)? + ); + + println!("{}", tera.render("code.html", &ctx)?); + + Ok(()) + } else { + eprintln!("Usage: {} template_glob", args().collect::>()[0]); + std::process::exit(64); + } +}