diff --git a/Cargo.lock b/Cargo.lock
@@ -35,6 +35,12 @@ dependencies = [
"winapi",
]
+[[package]]
+name = "anyhow"
+version = "1.0.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
+
[[package]]
name = "arc-swap"
version = "1.5.0"
@@ -1139,6 +1145,28 @@ dependencies = [
"minimal-lexical",
]
+[[package]]
+name = "nom_locate"
+version = "4.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37794436ca3029a3089e0b95d42da1f0b565ad271e4d3bb4bad0c7bb70b10605"
+dependencies = [
+ "bytecount",
+ "memchr",
+ "nom",
+]
+
+[[package]]
+name = "num-bigint"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
+dependencies = [
+ "autocfg 1.1.0",
+ "num-integer",
+ "num-traits",
+]
+
[[package]]
name = "num-integer"
version = "0.1.45"
@@ -1149,6 +1177,17 @@ dependencies = [
"num-traits",
]
+[[package]]
+name = "num-rational"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
+dependencies = [
+ "autocfg 1.1.0",
+ "num-integer",
+ "num-traits",
+]
+
[[package]]
name = "num-traits"
version = "0.2.15"
@@ -1600,6 +1639,7 @@ dependencies = [
name = "rgit"
version = "0.1.0"
dependencies = [
+ "anyhow",
"arc-swap",
"askama",
"axum",
@@ -1613,6 +1653,7 @@ dependencies = [
"moka",
"parking_lot",
"path-clean",
+ "rsass",
"serde",
"syntect",
"time 0.3.11",
@@ -1627,6 +1668,23 @@ dependencies = [
"uuid",
]
+[[package]]
+name = "rsass"
+version = "0.25.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ab003a9ff922b4d7b1f5f383bc54645887ad278f534dfc65e071096fcb30023"
+dependencies = [
+ "fastrand",
+ "lazy_static",
+ "nom",
+ "nom_locate",
+ "num-bigint",
+ "num-integer",
+ "num-rational",
+ "num-traits",
+ "tracing",
+]
+
[[package]]
name = "ryu"
version = "1.0.10"
diff --git a/Cargo.toml b/Cargo.toml
@@ -31,3 +31,7 @@ tracing = "0.1"
tracing-subscriber = "0.3"
unix_mode = "0.1"
uuid = { version = "1.1", features = ["v4"] }
+
+[build-dependencies]
+anyhow = "1.0"
+rsass = "0.25"
diff --git a/build.rs b/build.rs
@@ -0,0 +1,66 @@
+use anyhow::Context;
+use std::{
+ io::Write,
+ path::{Path, PathBuf},
+};
+
+#[derive(Copy, Clone)]
+pub struct Paths<'a> {
+ statics_in_dir: &'a Path,
+ statics_out_dir: &'a Path,
+}
+
+fn main() {
+ if let Err(e) = run() {
+ eprintln!("An error occurred within the rgit build script:\n\n{:?}", e);
+ std::process::exit(1);
+ }
+}
+
+fn run() -> anyhow::Result<()> {
+ let manifest_dir =
+ PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").context("CARGO_MANIFEST_DIR not set")?);
+ let statics_in_dir = manifest_dir.join("statics");
+
+ let out_dir = PathBuf::from(std::env::var("OUT_DIR").context("OUT_DIR not set by rustc")?);
+ let statics_out_dir = out_dir.join("statics");
+
+ let paths = Paths {
+ statics_in_dir: &statics_in_dir,
+ statics_out_dir: &statics_out_dir,
+ };
+
+ build_scss(paths).context("Failed to build CSS stylesheets")?;
+
+ Ok(())
+}
+
+fn build_scss(paths: Paths) -> anyhow::Result<()> {
+ let in_dir = paths.statics_in_dir.join("sass");
+ let out_dir = paths.statics_out_dir.join("css");
+ std::fs::create_dir_all(&out_dir).context("Failed to create output directory")?;
+
+ println!("cargo:rerun-if-changed={}", in_dir.display());
+
+ let input_file = in_dir.join("style.scss");
+ let output_file = out_dir.join("style.css");
+ let format = rsass::output::Format {
+ style: rsass::output::Style::Compressed,
+ ..rsass::output::Format::default()
+ };
+
+ let output_content =
+ rsass::compile_scss_path(&input_file, format).context("Failed to compile SASS")?;
+
+ let mut output_file = std::fs::OpenOptions::new()
+ .write(true)
+ .create(true)
+ .truncate(true)
+ .open(output_file)
+ .context("Failed to open output file")?;
+ output_file
+ .write_all(&output_content)
+ .context("Failed to write compiled CSS to output")?;
+
+ Ok(())
+}
diff --git a/src/git.rs b/src/git.rs
@@ -1,14 +1,17 @@
use std::{
borrow::Cow,
collections::BTreeMap,
+ fmt::Write,
path::{Path, PathBuf},
sync::Arc,
time::Duration,
- fmt::Write,
};
use arc_swap::ArcSwapOption;
-use git2::{BranchType, DiffFormat, DiffLineType, DiffOptions, DiffStatsFormat, ObjectType, Oid, Repository, Signature};
+use git2::{
+ BranchType, DiffFormat, DiffLineType, DiffOptions, DiffStatsFormat, ObjectType, Oid,
+ Repository, Signature,
+};
use moka::future::Cache;
use parking_lot::Mutex;
use syntect::html::{ClassStyle, ClassedHTMLGenerator};
@@ -96,7 +99,12 @@ pub struct OpenRepository {
}
impl OpenRepository {
- pub async fn path(self: Arc<Self>, path: Option<PathBuf>, tree_id: Option<&str>, branch: Option<String>) -> PathDestination {
+ pub async fn path(
+ self: Arc<Self>,
+ path: Option<PathBuf>,
+ tree_id: Option<&str>,
+ branch: Option<String>,
+ ) -> PathDestination {
let tree_id = tree_id.map(Oid::from_str).transpose().unwrap();
tokio::task::spawn_blocking(move || {
@@ -120,7 +128,8 @@ impl OpenRepository {
let name = item.name().unwrap().to_string();
let path = path.clone().join(&name);
- let extension = path.extension()
+ let extension = path
+ .extension()
.or(path.file_name())
.unwrap()
.to_string_lossy();
@@ -581,7 +590,10 @@ fn format_file(content: &[u8], extension: &str, syntax_set: &SyntaxSet) -> Strin
.unwrap();
}
- format!("<code>{}</code>", html_generator.finalize().replace('\n', "</code>\n<code>"))
+ format!(
+ "<code>{}</code>",
+ html_generator.finalize().replace('\n', "</code>\n<code>")
+ )
}
#[instrument(skip(diff, syntax_set))]
diff --git a/src/main.rs b/src/main.rs
@@ -1,11 +1,14 @@
#![deny(clippy::pedantic)]
-use axum::{body::Body, handler::Handler, http::HeaderValue, response::Response, routing::get, Extension, Router, http};
-use bat::assets::HighlightingAssets;
-use std::sync::Arc;
use askama::Template;
use axum::http::StatusCode;
use axum::response::IntoResponse;
+use axum::{
+ body::Body, handler::Handler, http, http::HeaderValue, response::Response, routing::get,
+ Extension, Router,
+};
+use bat::assets::HighlightingAssets;
+use std::sync::Arc;
use syntect::html::ClassStyle;
use tower_layer::layer_fn;
@@ -38,7 +41,10 @@ async fn main() {
.route("/", get(methods::index::handle))
.route(
"/style.css",
- get(static_css(include_bytes!("../statics/style.css"))),
+ get(static_css(include_bytes!(concat!(
+ env!("OUT_DIR"),
+ "/statics/css/style.css"
+ )))),
)
.route("/highlight.css", get(static_css(css)))
.fallback(methods::repo::service.into_service())
@@ -54,8 +60,10 @@ async fn main() {
fn static_css(content: &'static [u8]) -> impl Handler<()> {
move || async move {
let mut resp = Response::new(Body::from(content));
- resp.headers_mut()
- .insert(http::header::CONTENT_TYPE, HeaderValue::from_static("text/css"));
+ resp.headers_mut().insert(
+ http::header::CONTENT_TYPE,
+ HeaderValue::from_static("text/css"),
+ );
resp
}
}
diff --git a/src/methods/index.rs b/src/methods/index.rs
@@ -4,7 +4,7 @@ use axum::Extension;
use std::sync::Arc;
use super::filters;
-use crate::{git::RepositoryMetadataList, Git, into_response};
+use crate::{git::RepositoryMetadataList, into_response, Git};
#[derive(Template)]
#[template(path = "index.html")]
@@ -15,7 +15,5 @@ pub struct View {
pub async fn handle(Extension(git): Extension<Arc<Git>>) -> Response {
let repositories = git.fetch_repository_metadata().await;
- into_response(&View {
- repositories,
- })
+ into_response(&View { repositories })
}
diff --git a/src/methods/repo.rs b/src/methods/repo.rs
@@ -1,9 +1,9 @@
+use std::fmt::{Display, Formatter};
use std::{
ops::Deref,
path::{Path, PathBuf},
sync::Arc,
};
-use std::fmt::{Display, Formatter};
use askama::Template;
use axum::{
@@ -19,7 +19,7 @@ use tower::{util::BoxCloneService, Service};
use super::filters;
use crate::git::{DetailedTag, FileWithContent, PathDestination, Refs, TreeItem};
-use crate::{git::Commit, layers::UnwrapInfallible, Git, into_response};
+use crate::{git::Commit, into_response, layers::UnwrapInfallible, Git};
#[derive(Clone)]
pub struct Repository(pub PathBuf);
@@ -294,7 +294,10 @@ pub async fn handle_tree(
let open_repo = git.repo(repository_path).await;
- match open_repo.path(child_path, query.id.as_deref(), query.branch.clone()).await {
+ match open_repo
+ .path(child_path, query.id.as_deref(), query.branch.clone())
+ .await
+ {
PathDestination::Tree(items) => into_response(&TreeView { repo, items, query }),
PathDestination::File(file) => into_response(&FileView { repo, file }),
}
diff --git a/statics/sass/code.scss b/statics/sass/code.scss
@@ -0,0 +1,29 @@
+pre {
+ height: 100%;
+ width: 100%;
+ margin: 0;
+ overflow: auto;
+ counter-reset: line;
+
+ &.h2-first-line {
+ &::first-line {
+ font-family: sans-serif;
+ font-size: 1.5em;
+ font-weight: bold;
+ }
+ }
+
+ code {
+ counter-increment: line;
+
+ &::before {
+ content: counter(line);
+ display: inline-block;
+ width: 2em;
+ padding: 0 1em 0.3em 0;
+ margin-right: .5em;
+ color: #888;
+ -webkit-user-select: none;
+ }
+ }
+}
diff --git a/statics/sass/diff.scss b/statics/sass/diff.scss
@@ -0,0 +1,37 @@
+.diff-add-line::before, .diff-remove-line::before, .diff-context::before {
+ display: inline-block;
+ color: #888;
+ -webkit-user-select: none;
+}
+
+.diff-file-header {
+ font-weight: bold;
+}
+
+.diff-file-header > span > span {
+ font-weight: normal;
+}
+
+.diff-add-line {
+ background: #e6ffec;
+ display: block;
+
+ &::before {
+ content: '+ ';
+ }
+}
+
+.diff-remove-line {
+ background: #ffebe9;
+ display: block;
+
+ &::before {
+ content: '- ';
+ }
+}
+
+.diff-context {
+ &::before {
+ content: ' ';
+ }
+}
\ No newline at end of file
diff --git a/statics/sass/style.scss b/statics/sass/style.scss
@@ -0,0 +1,55 @@
+@import 'util';
+@import 'diff';
+@import 'tables';
+@import 'code';
+
+body {
+ font-family: sans-serif;
+ font-size: 0.9rem;
+}
+
+header {
+ border-bottom: solid 1px #ccc;
+}
+
+nav {
+ margin-top: 2rem;
+ border-bottom: solid 3px #ccc;
+
+ a {
+ padding: 2px 0.75em;
+ color: #777;
+ font-size: 110%;
+
+ &.active {
+ color: #000;
+ background-color: #ccc;
+ }
+ }
+}
+
+main {
+ padding: 2rem;
+ margin: 0;
+ border-bottom: solid 3px #ccc;
+}
+
+footer {
+ margin-top: 0.5em;
+ text-align: center;
+ font-size: 80%;
+ color: #ccc;
+}
+
+a {
+ text-decoration: none;
+ color: blue;
+
+ &.no-style {
+ color: inherit;
+ }
+
+ &:hover {
+ text-decoration: underline;
+ }
+}
diff --git a/statics/sass/tables.scss b/statics/sass/tables.scss
@@ -0,0 +1,56 @@
+table {
+ border-collapse: collapse;
+
+ th {
+ text-align: left;
+ }
+}
+
+table.repositories {
+ width: 100%;
+
+ a {
+ color: black;
+
+ &:hover {
+ color: #00f;
+ }
+ }
+
+ tbody {
+ tr {
+ &:nth-child(odd) {
+ background: #f7f7f7;
+ }
+
+ &.has-parent td:first-of-type {
+ padding-left: 1rem;
+ }
+
+ td.repo-section {
+ font-style: italic;
+ color: #888;
+ }
+
+ &.separator {
+ background: white !important;
+ height: 1rem;
+ }
+ }
+
+ pre {
+ margin: 0;
+ }
+ }
+
+ .nested-tree {
+ color: blue !important;
+ font-weight: bold;
+ }
+}
+
+table.commit-info {
+ td, th {
+ padding: 0.1em 1em 0.1em 0.1em;
+ }
+}
\ No newline at end of file
diff --git a/statics/sass/util.scss b/statics/sass/util.scss
@@ -0,0 +1,11 @@
+.mt-2 {
+ margin-top: 2rem;
+}
+
+.text-center {
+ text-align: center;
+}
+
+.no-hover:hover {
+ text-decoration: none;
+}
diff --git a/statics/style.css b/statics/style.css
@@ -1,160 +0,0 @@
-body {
- font-family: sans-serif;
- font-size: 10pt;
-}
-
-main {
- padding: 2rem;
- margin: 0;
- border-bottom: solid 3px #ccc;
-}
-
-header {
- border-bottom: solid 1px #ccc;
-}
-
-footer {
- margin-top: 0.5em;
- text-align: center;
- font-size: 80%;
- color: #ccc;
-}
-
-a {
- text-decoration: none;
- color: blue;
-}
-
-a.no-style {
- color: inherit;
-}
-
-a:hover {
- text-decoration: underline;
-}
-
-.mt-2 {
- margin-top: 2rem;
-}
-
-.text-center {
- text-align: center;
-}
-
-.no-hover:hover {
- text-decoration: none;
-}
-
-table { border-collapse: collapse; }
-table.repositories { width: 100%; }
-table.repositories a { color: black; }
-table.repositories a:hover { color: #00f; }
-table th { text-align: left; }
-.repo-section { font-style: italic; color: #888; }
-
-table.repositories tbody tr:nth-child(odd) {
- background: #f7f7f7;
-}
-
-table tbody tr.has-parent td:first-of-type {
- padding-left: 1rem;
-}
-
-tr.separator {
- background: white !important;
- height: 1rem;
-}
-
-table pre { margin: 0; }
-
-table.commit-info td, table.commit-info th { padding: 0.1em 1em 0.1em 0.1em; }
-
-nav {
- margin-top: 2rem;
- border-bottom: solid 3px #ccc;
-}
-
-nav a {
- padding: 2px 0.75em;
- color: #777;
- font-size: 110%;
- text-decoration: none;
-}
-
-nav a:hover {
- text-decoration: underline;
-}
-
-nav a.active {
- color: #000;
- background-color: #ccc;
-}
-
-pre.h2-first-line::first-line {
- font-family: sans-serif;
- font-size: 1.5em;
- font-weight: bold;
-}
-
-pre {
- height: 100%;
- width: 100%;
- margin: 0;
- overflow: auto;
- counter-reset: line;
-}
-
-code {
- counter-increment: line;
-}
-
-code::before {
- content: counter(line);
- display: inline-block;
- width: 2em;
- padding: 0 1em 0.3em 0;
- margin-right: .5em;
- color: #888;
- -webkit-user-select: none;
-}
-
-.diff-file-header {
- font-weight: bold;
-}
-
-.diff-file-header > span > span {
- font-weight: normal;
-}
-
-.diff-add-line::before, .diff-remove-line::before, .diff-context::before {
- display: inline-block;
- color: #888;
- -webkit-user-select: none;
-}
-
-.diff-add-line {
- background: #e6ffec;
- display: block;
-}
-
-.diff-add-line::before {
- content: '+ ';
-}
-
-.diff-remove-line {
- background: #ffebe9;
- display: block;
-}
-
-.diff-remove-line::before {
- content: '- ';
-}
-
-.diff-context::before {
- content: ' ';
-}
-
-.nested-tree {
- color: blue !important;
- font-weight: bold;
-}