mini_static/
css_bundler.rs1use std::path::{Path, PathBuf};
2use std::fs;
3
4use crate::bundle;
5use crate::minify;
6
7pub async fn bundle_directory_css(
27 src_dir: &Path,
28 output_path: &Path,
29) -> Result<(), CssBundlerError> {
30 let src_dir_canon = src_dir.canonicalize().map_err(|e| {
31 CssBundlerError::ReadSource {
32 path: src_dir.to_path_buf(),
33 reason: e.to_string(),
34 }
35 })?;
36
37 let css_files = find_css_files(&src_dir_canon).map_err(|e| {
38 CssBundlerError::ReadSource {
39 path: src_dir.to_path_buf(),
40 reason: e.to_string(),
41 }
42 })?;
43
44 if css_files.is_empty() {
45 return Err(CssBundlerError::NoFilesFound(src_dir.to_path_buf()));
46 }
47
48 let mut bundled_content = String::new();
49
50 for css_file in &css_files {
51 let (bytes, _deps) = bundle::bundle_and_minify_css(&[src_dir_canon.clone()], css_file)
52 .await
53 .map_err(|e| CssBundlerError::Bundle(format!("{:?}", e)))?;
54
55 bundled_content.push_str(&String::from_utf8_lossy(&bytes));
56 }
57
58 let bundled_bytes_final = minify::minify(bundled_content.as_bytes(), crate::reload::ChangeType::Css)
59 .map_err(|e| CssBundlerError::Bundle(format!("Minification failed: {:?}", e)))?;
60
61 if let Some(parent) = output_path.parent() {
62 fs::create_dir_all(parent).map_err(|e| {
63 CssBundlerError::WriteOutput {
64 path: output_path.to_path_buf(),
65 reason: e.to_string(),
66 }
67 })?;
68 }
69
70 fs::write(output_path, &bundled_bytes_final).map_err(|e| {
71 CssBundlerError::WriteOutput {
72 path: output_path.to_path_buf(),
73 reason: e.to_string(),
74 }
75 })?;
76
77 Ok(())
78}
79
80fn find_css_files(dir: &Path) -> std::io::Result<Vec<PathBuf>> {
82 let mut css_files = Vec::new();
83 let mut dirs = vec![dir.to_path_buf()];
84
85 while let Some(current_dir) = dirs.pop() {
86 let entries = fs::read_dir(¤t_dir)?;
87 for entry in entries {
88 let entry = entry?;
89 let path = entry.path();
90 let file_type = entry.file_type()?;
91
92 if file_type.is_dir() {
93 dirs.push(path);
94 } else if file_type.is_file() {
95 if path.extension().and_then(|s| s.to_str()) == Some("css") {
96 css_files.push(path);
97 }
98 }
99 }
100 }
101
102 css_files.sort();
103 Ok(css_files)
104}
105
106#[derive(Debug)]
108pub enum CssBundlerError {
109 ReadSource { path: PathBuf, reason: String },
111 WriteOutput { path: PathBuf, reason: String },
113 NoFilesFound(PathBuf),
115 Bundle(String),
117}
118
119impl std::fmt::Display for CssBundlerError {
120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121 match self {
122 CssBundlerError::ReadSource { path, reason } => {
123 write!(f, "failed to read source dir {}: {}", path.display(), reason)
124 }
125 CssBundlerError::WriteOutput { path, reason } => {
126 write!(f, "failed to write output file {}: {}", path.display(), reason)
127 }
128 CssBundlerError::NoFilesFound(path) => {
129 write!(f, "no CSS files found in {}", path.display())
130 }
131 CssBundlerError::Bundle(msg) => {
132 write!(f, "CSS bundling failed: {}", msg)
133 }
134 }
135 }
136}
137
138impl std::error::Error for CssBundlerError {}
139