rumtk_web/css/
mod.rs

1/*
2 * rumtk attempts to implement HL7 and medical protocols for interoperability in medicine.
3 * This toolkit aims to be reliable, simple, performant, and standards compliant.
4 * Copyright (C) 2025  Luis M. Santos, M.D.
5 * Copyright (C) 2025  Nick Stephenson
6 * Copyright (C) 2025  Ethan Dixon
7 * Copyright (C) 2025  MedicalMasses L.L.C.
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 */
23use crate::utils::packaging::{minify_asset, Asset};
24use rumtk_core::hash::has_same_hash;
25use rumtk_core::strings::{RUMString, RUMStringConversions};
26use std::{fs, path};
27
28mod animations;
29mod basic;
30mod default;
31mod fonts;
32mod gap;
33mod index;
34mod layout;
35mod theme;
36
37pub const DEFAULT_OUT_CSS_DIR: &str = "./static/css";
38pub const DEFAULT_OUT_CSS: &str = "bundle.min.css";
39
40pub fn bundle_css(sources: &Vec<String>, out_dir: &str, out_file: &str) {
41    let mut css: RUMString = RUMString::default();
42
43    css += theme::THEME;
44    css += index::BODY;
45    css += basic::BASIC_CSS;
46    css += default::DEFAULT_CSS;
47    css += fonts::FONTS_CSS;
48    css += gap::GAP_CSS;
49    css += animations::ANIMATIONS_CSS;
50    css += layout::LAYOUT;
51
52    for source in sources {
53        let css_data = fs::read_to_string(source).unwrap_or_default();
54        css += &css_data;
55    }
56
57    fs::create_dir_all(out_dir).unwrap_or_default();
58
59    let out_path = path::Path::new(out_dir)
60        .join(out_file)
61        .with_extension("css")
62        .to_str()
63        .expect("Could not create path to CSS file!")
64        .to_string();
65
66    let minified = minify_asset(Asset::CSS(&css))
67        .expect("Failed to minify the CSS contents!")
68        .to_rumstring();
69
70    let file_exists = fs::exists(&out_path).unwrap_or_default();
71    let skip_write_css = file_exists
72        && has_same_hash(
73            &minified,
74            &fs::read_to_string(&out_path)
75                .unwrap_or_default()
76                .to_rumstring(),
77        );
78
79    if !skip_write_css {
80        println!("Generated minified CSS file!");
81        fs::write(&out_path, minified).expect("Failed to write to CSS file!");
82    }
83}
84
85pub fn collect_css_sources(root: &str, depth: u8) -> Vec<String> {
86    let mut files = Vec::<String>::new();
87
88    let dirs = match fs::read_dir(root) {
89        Ok(dirs) => dirs,
90        Err(_) => return files,
91    };
92
93    for dir_entry in dirs {
94        let dir = dir_entry.unwrap();
95        let dir_name = dir.file_name().into_string().unwrap();
96        let dir_path = dir.path().to_str().unwrap().to_string();
97        if dir_name.ends_with(".css") && dir_name != DEFAULT_OUT_CSS {
98            files.push(dir_path.clone());
99        }
100
101        if depth == 255 {
102            return files;
103        }
104
105        if dir.file_type().unwrap().is_dir() {
106            files.extend(collect_css_sources(&dir_path, depth + 1));
107        }
108    }
109
110    files
111}
112
113#[macro_export]
114macro_rules! rumtk_web_compile_css_bundle {
115    (  ) => {{
116        use $crate::css::{bundle_css, collect_css_sources};
117        use $crate::css::{DEFAULT_OUT_CSS, DEFAULT_OUT_CSS_DIR};
118        let sources = collect_css_sources(DEFAULT_OUT_CSS_DIR, 0);
119        bundle_css(&sources, DEFAULT_OUT_CSS_DIR, DEFAULT_OUT_CSS);
120    }};
121    ( $static_dir_path:expr ) => {{
122        use $crate::css::{bundle_css, collect_css_sources};
123        use $crate::css::{DEFAULT_OUT_CSS, DEFAULT_OUT_CSS_DIR};
124        let sources = collect_css_sources($static_dir_path, 0);
125        bundle_css(&sources, DEFAULT_OUT_CSS_DIR, DEFAULT_OUT_CSS);
126    }};
127}