From d66a989bd62ca656c30bcb3a3e7a90bacdaa460a Mon Sep 17 00:00:00 2001 From: Jordan Doyle Date: Sat, 16 Jul 2022 11:53:12 +0100 Subject: [PATCH] Port CSS to SCSS --- Cargo.lock | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 4 ++++ build.rs | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/git.rs | 22 +++++++++++++++++----- src/main.rs | 20 ++++++++++++++------ statics/style.css | 160 -------------------------------------------------------------------------------- src/methods/index.rs | 6 ++---- src/methods/repo.rs | 9 ++++++--- statics/sass/code.scss | 29 +++++++++++++++++++++++++++++ statics/sass/diff.scss | 37 +++++++++++++++++++++++++++++++++++++ statics/sass/style.scss | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ statics/sass/tables.scss | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ statics/sass/util.scss | 11 +++++++++++ 13 files changed, 355 insertions(+), 178 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 344620e..03badbd 100644 --- a/Cargo.lock +++ a/Cargo.lock @@ -36,6 +36,12 @@ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1137,6 +1143,28 @@ dependencies = [ "memchr", "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]] @@ -1150,6 +1178,17 @@ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1600,6 +1639,7 @@ name = "rgit" version = "0.1.0" dependencies = [ + "anyhow", "arc-swap", "askama", "axum", @@ -1613,6 +1653,7 @@ "moka", "parking_lot", "path-clean", + "rsass", "serde", "syntect", "time 0.3.11", @@ -1625,6 +1666,23 @@ "tracing-subscriber", "unix_mode", "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]] diff --git a/Cargo.toml b/Cargo.toml index 4f78ebb..90a1901 100644 --- a/Cargo.toml +++ a/Cargo.toml @@ -31,3 +31,7 @@ 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 new file mode 100644 index 0000000..cbcc334 100644 --- /dev/null +++ a/build.rs @@ -1,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 index 95c6259..d178954 100644 --- a/src/git.rs +++ a/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 @@ } impl OpenRepository { - pub async fn path(self: Arc, path: Option, tree_id: Option<&str>, branch: Option) -> PathDestination { + pub async fn path( + self: Arc, + path: Option, + tree_id: Option<&str>, + branch: Option, + ) -> PathDestination { let tree_id = tree_id.map(Oid::from_str).transpose().unwrap(); tokio::task::spawn_blocking(move || { @@ -120,7 +128,8 @@ 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 @@ .unwrap(); } - format!("{}", html_generator.finalize().replace('\n', "\n")) + format!( + "{}", + html_generator.finalize().replace('\n', "\n") + ) } #[instrument(skip(diff, syntax_set))] diff --git a/src/main.rs b/src/main.rs index 24621e8..2e7d5f5 100644 --- a/src/main.rs +++ a/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 @@ .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 @@ 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/statics/style.css b/statics/style.css deleted file mode 100644 index 5854b40..0000000 100644 --- a/statics/style.css +++ /dev/null @@ -1,160 +1,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; -} diff --git a/src/methods/index.rs b/src/methods/index.rs index 2d02a87..9497930 100644 --- a/src/methods/index.rs +++ a/src/methods/index.rs @@ -1,10 +1,10 @@ use askama::Template; use axum::response::Response; 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 async fn handle(Extension(git): Extension>) -> 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 index afe96cb..0cf36ec 100644 --- a/src/methods/repo.rs +++ a/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 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 @@ 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 new file mode 100644 index 0000000..cc304b5 100644 --- /dev/null +++ a/statics/sass/code.scss @@ -1,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 new file mode 100644 index 0000000..f959b52 100644 --- /dev/null +++ a/statics/sass/diff.scss @@ -1,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: ' '; + } +}diff --git a/statics/sass/style.scss b/statics/sass/style.scss new file mode 100644 index 0000000..3b213cb 100644 --- /dev/null +++ a/statics/sass/style.scss @@ -1,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 new file mode 100644 index 0000000..920fd2e 100644 --- /dev/null +++ a/statics/sass/tables.scss @@ -1,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; + } +}diff --git a/statics/sass/util.scss b/statics/sass/util.scss new file mode 100644 index 0000000..6a71f20 100644 --- /dev/null +++ a/statics/sass/util.scss @@ -1,0 +1,11 @@ +.mt-2 { + margin-top: 2rem; +} + +.text-center { + text-align: center; +} + +.no-hover:hover { + text-decoration: none; +} -- rgit 0.1.3