Skip to content

Commit

Permalink
build css with build script, embed static files into binary
Browse files Browse the repository at this point in the history
  • Loading branch information
Joeyh021 committed Aug 25, 2023
1 parent a307aa9 commit ea9fe2c
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 2 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
/target
.env
.env
# tailwind cli binary
tailwindcss
# dont check in the build
static/output.css
147 changes: 147 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,9 @@ tower-http = { version = "0.4.0", features = ["fs", "catch-panic"] }
tracing = "0.1"
tracing-subscriber = "0.3.17"
tracing-attributes = "0.1.26"
rust-embed = { version = "8.0.0", features = ["include-exclude"] }
mime_guess = "2.0.4"


[build-dependencies]
which = "4.4.0"
38 changes: 38 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use std::env;
use std::path::{Path, PathBuf};
use which::which;

// Example custom build script.
fn main() {
// Tell Cargo that if the given file changes, to rerun this build script.
println!("cargo:rerun-if-changed=templates/*");
println!("cargo:rerun-if-changed=static/styles.css");
println!("cargo:rerun-if-changed=tailwind.config.js");
if let Some(exe) = find_tailwind() {
run_tailwind(&exe)
} else {
panic!("Tailwind CSS executable not found. Please download tailwindcss and either place it on your PATH or in the root of this Cargo project. https://tailwindcss.com/blog/standalone-cli ")
}
}

fn find_tailwind() -> Option<PathBuf> {
which("tailwindcss").ok().or_else(|| {
let pwd = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let path = pwd.join("tailwindcss");
path.exists().then_some(path.to_path_buf())
})
}

fn run_tailwind(exe: &Path) {
let result = std::process::Command::new(exe)
.arg("-i")
.arg("static/styles.css")
.arg("-o")
.arg("static/output.css")
.arg("--minify")
.output()
.map(|_| ());
if let Err(e) = result {
panic!("Failed to run tailwindcss: {}", e)
}
}
3 changes: 2 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use tower_http::services::ServeDir;
mod config;
mod db;
mod routes;
mod statics;
mod types;

/// Struct containing application state that may be needed by any handler
Expand Down Expand Up @@ -59,7 +60,7 @@ async fn main() -> anyhow::Result<()> {
.nest("/api", routes::api_routes(api_secret))
.nest("/auth", routes::auth_routes())
.nest("/app", routes::app::app_routes())
.nest_service("/static", get_service(ServeDir::new("./static/")))
.route("/static/*file", get(statics::static_handler))
.with_state(state)
.fallback(|| async { (StatusCode::NOT_FOUND, "Not Found") })
.layer(session_layer)
Expand Down
38 changes: 38 additions & 0 deletions src/statics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use axum::{
http::{header, StatusCode, Uri},
response::{IntoResponse, Response},
};
use rust_embed::RustEmbed;

#[derive(RustEmbed)]
#[folder = "static/"]
pub struct Asset;

pub struct StaticFile<T>(pub T);

/// from https://github.com/pyrossh/rust-embed/blob/master/examples/axum.rs
/// convert static files to axum responses
impl<T> IntoResponse for StaticFile<T>
where
T: Into<String>,
{
fn into_response(self) -> Response {
let path = self.0.into();
dbg!(Asset::iter().collect::<Vec<_>>());
match Asset::get(path.as_str()) {
Some(content) => {
let mime = mime_guess::from_path(path).first_or_octet_stream();
([(header::CONTENT_TYPE, mime.as_ref())], content.data).into_response()
}
None => (StatusCode::NOT_FOUND, "404 Not Found").into_response(),
}
}
}

// We use a wildcard matcher ("/static/*file") to match against everything
// within our defined assets directory. This is the directory on our Asset
// struct, where folder = "static/".
pub async fn static_handler(uri: Uri) -> impl IntoResponse {
dbg!("in static handler");
StaticFile(uri.path().trim_start_matches("/static/").to_string())
}

0 comments on commit ea9fe2c

Please sign in to comment.