oranda/site/layout/
css.rs1use std::env;
2use std::sync::RwLock;
3
4use crate::errors::*;
5
6use crate::config::style::ORANDA_CSS_TAG;
7use axoasset::{Asset, LocalAsset};
8use camino::Utf8Path;
9use minifier::css;
10
11static CSS_CACHE: RwLock<Vec<CssItem>> = RwLock::new(Vec::new());
12
13struct CssItem {
14 release_tag: String,
15 contents: String,
16}
17
18fn concat_minify(css_files: &[String]) -> Result<String> {
19 let mut css = String::new();
20 for file in css_files {
21 let future = Asset::load_string(file);
22 let unminified = tokio::runtime::Handle::current().block_on(future)?;
23 let minified = match css::minify(&unminified) {
24 Ok(css) => Ok(css),
25 Err(e) => Err(OrandaError::Other(e.to_string())),
26 };
27 css = format!("{css}/* {file} */{minified}", minified = minified?);
28 }
29
30 Ok(css)
31}
32
33pub fn get_css_link(path_prefix: &Option<String>, release_tag: &str) -> Result<String> {
34 let filename = get_css_filename(release_tag);
35 Ok(crate::site::link::generate_relative(path_prefix, &filename))
36}
37
38pub fn place_css(dist_dir: &str, release_tag: &str) -> Result<()> {
41 if release_tag == ORANDA_CSS_TAG {
43 #[cfg(css = "tailwind")]
45 {
46 oranda_generate_css::build_css(Utf8Path::new(dist_dir))?;
47 }
48
49 #[cfg(css = "file")]
51 {
52 let css = include_str!("../../../oranda-css/dist/oranda.css");
53 LocalAsset::write_new_all(css, format!("{dist_dir}/oranda-{release_tag}.css"))?;
54 }
55
56 #[cfg(css = "fetch")]
58 {
59 fetch_css(dist_dir, release_tag)?;
60 }
61 Ok(())
62 } else {
63 fetch_css(dist_dir, release_tag)
66 }
67}
68
69fn fetch_css(dist_dir: &str, release_tag: &str) -> Result<()> {
70 match env::var("ORANDA_CSS") {
71 Ok(path) => {
72 let msg = format!("Overriding oranda_css path with {}", &path);
73 tracing::warn!("{}", &msg);
74 LocalAsset::copy(&path, dist_dir)?;
75 Ok(())
76 }
77 Err(_) => {
78 let filename = format!("oranda-{release_tag}.css");
79 let dest_path = Utf8Path::new(dist_dir).join(filename);
80
81 let cache_val = {
83 let cache = CSS_CACHE.read().expect("CSS Cache should not be poisoned");
84 cache
85 .iter()
86 .find(|elem| elem.release_tag.as_str() == release_tag)
87 .map(|elem| elem.contents.clone())
88 };
89
90 let oranda_css_response = if let Some(c) = cache_val {
91 c
93 } else {
94 let fresh =
96 tokio::runtime::Handle::current().block_on(fetch_oranda(release_tag))?;
97
98 let mut cache = CSS_CACHE.write().expect("CSS Cache should not be poisoned");
99 cache.push(CssItem {
100 release_tag: release_tag.to_string(),
101 contents: fresh.clone(),
102 });
103 fresh
104 };
105
106 LocalAsset::write_new_all(&oranda_css_response, dest_path)?;
107 Ok(())
108 }
109 }
110}
111
112async fn fetch_oranda(release_tag: &str) -> Result<String> {
113 let oranda_css_request =
114 octolotl::request::ReleaseAsset::new("axodotdev", "oranda", release_tag, "oranda.css");
115 Ok(octolotl::Request::send(&oranda_css_request, true)
116 .await?
117 .text()
118 .await?)
119}
120
121fn get_css_filename(release_tag: &str) -> String {
122 if (release_tag == ORANDA_CSS_TAG && cfg!(css = "tailwind")) || env::var("ORANDA_CSS").is_ok() {
123 "oranda.css".into()
124 } else {
125 format!("oranda-{release_tag}.css")
126 }
127}
128
129pub fn write_additional_css(additional_css: &[String], dist_dir: &Utf8Path) -> Result<()> {
130 let minified_css = concat_minify(additional_css)?;
131
132 LocalAsset::write_new(&minified_css, dist_dir.join("custom.css"))?;
133 Ok(())
134}