Skip to main content

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. <lsantos@medicalmasses.com>
5 * Copyright (C) 2025  Ethan Dixon
6 * Copyright (C) 2025  MedicalMasses L.L.C. <contact@medicalmasses.com>
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20 */
21use crate::utils::packaging::{minify_asset, Asset};
22use rumtk_core::hash::has_same_hash;
23use rumtk_core::strings::{RUMString, RUMStringConversions};
24use std::{fs, path};
25mod defaults;
26
27pub use defaults::*;
28use rumtk_core::base::RUMResult;
29
30pub fn bundle_css(sources: &Vec<String>, out_dir: &str, out_file: &str, skip_default_css: bool) -> RUMResult<()> {
31    let mut css: RUMString = match skip_default_css {
32        true => RUMString::default(),
33        false => DEFAULT_CSS.to_string(),
34    };
35
36    for source in sources {
37        let css_data = fs::read_to_string(source).unwrap_or_default();
38        css += &css_data;
39    }
40
41    fs::create_dir_all(out_dir).unwrap_or_default();
42
43    let path = path::Path::new(out_dir)
44        .join(out_file)
45        .with_extension("css");
46    let out_path = match path
47        .to_str()
48    {
49        Some(path) => path,
50        None => return Err("Could not create path to CSS file!".into()),
51    };
52
53    let minified = match minify_asset(Asset::CSS(&css))
54    {
55        Ok(minified) => minified,
56        Err(err) => return Err(format!("Failed to minify the CSS contents! {}", err).into()),
57    };
58
59    let file_exists = fs::exists(&out_path).unwrap_or_default();
60    let skip_write_css = file_exists
61        && has_same_hash(
62            &minified,
63            &fs::read_to_string(&out_path)
64                .unwrap_or_default()
65                .to_string(),
66        );
67
68    if !skip_write_css {
69        println!("Generated minified CSS file!");
70        match fs::write(&out_path, minified){
71            Ok(_) => (),
72            Err(err) => return Err(format!("Failed to write to CSS file! {}", err).into()),
73        };
74    }
75    Ok(())
76}
77
78pub fn collect_css_sources(root: &str, depth: u8) -> Vec<String> {
79    let mut files = Vec::<String>::new();
80
81    let dirs = match fs::read_dir(root) {
82        Ok(dirs) => dirs,
83        Err(_) => return files,
84    };
85
86    for dir_entry in dirs {
87        let dir = dir_entry.unwrap();
88        let dir_name = dir.file_name().into_string().unwrap();
89        let dir_path = dir.path().to_str().unwrap().to_string();
90        if dir_name.ends_with(".css") && dir_name != DEFAULT_OUT_CSS {
91            files.push(dir_path.clone());
92        }
93
94        if depth == 255 {
95            return files;
96        }
97
98        if dir.file_type().unwrap().is_dir() {
99            files.extend(collect_css_sources(&dir_path, depth + 1));
100        }
101    }
102
103    files
104}
105
106#[macro_export]
107macro_rules! rumtk_web_compile_css_bundle {
108    (  ) => {{
109        use $crate::css::DEFAULT_OUT_CSS_DIR;
110        let sources = collect_css_sources(DEFAULT_OUT_CSS_DIR, 0);
111        rumtk_web_compile_css_bundle!(DEFAULT_OUT_CSS_DIR, true);
112    }};
113    ( $static_dir_path:expr ) => {{
114        rumtk_web_compile_css_bundle!($static_dir_path, true);
115    }};
116    ( $static_dir_path:expr, $skip_default_css:expr ) => {{
117        use $crate::css::{bundle_css, collect_css_sources};
118        use $crate::css::{DEFAULT_OUT_CSS, DEFAULT_OUT_CSS_DIR};
119        let sources = collect_css_sources($static_dir_path, 0);
120        bundle_css(
121            &sources,
122            DEFAULT_OUT_CSS_DIR,
123            DEFAULT_OUT_CSS,
124            $skip_default_css,
125        );
126    }};
127}