use thiserror::Error;
use crate::{Blueprint, error::HauchiwaError, graph::Handle, loader::GlobAssetsTask};
#[derive(Debug, Error)]
pub enum StyleError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Sass compilation error: {0}")]
Sass(#[from] Box<grass::Error>),
#[error("Build error: {0}")]
Build(#[from] crate::error::BuildError),
}
#[derive(Debug, Clone)]
pub struct Stylesheet {
pub path: camino::Utf8PathBuf,
}
pub struct CssLoader<'a, G>
where
G: Send + Sync,
{
blueprint: &'a mut Blueprint<G>,
entry_globs: Vec<&'static str>,
watch_globs: Vec<&'static str>,
minify: bool,
}
impl<'a, G> CssLoader<'a, G>
where
G: Send + Sync + 'static,
{
fn new(blueprint: &'a mut Blueprint<G>) -> Self {
Self {
blueprint,
entry_globs: Vec::new(),
watch_globs: Vec::new(),
minify: true,
}
}
pub fn entry(mut self, glob: &'static str) -> Self {
self.entry_globs.push(glob);
self
}
pub fn watch(mut self, glob: &'static str) -> Self {
self.watch_globs.push(glob);
self
}
pub fn minify(mut self, minify: bool) -> Self {
self.minify = minify;
self
}
pub fn register(self) -> Result<Handle<super::Assets<Stylesheet>>, HauchiwaError> {
let watch_globs = if self.watch_globs.is_empty() {
self.entry_globs.clone()
} else {
self.watch_globs
};
let minify = self.minify;
Ok(self.blueprint.add_task_opaque(GlobAssetsTask::new(
self.entry_globs,
watch_globs,
move |_, store, input| {
let style = if minify {
grass::OutputStyle::Compressed
} else {
grass::OutputStyle::Expanded
};
let options = grass::Options::default().style(style);
let data =
grass::from_path(&input.path, &options).map_err(StyleError::Sass)?;
let path = store
.save(data.as_bytes(), "css")
.map_err(StyleError::Build)?;
Ok((input.path, Stylesheet { path }))
},
)?))
}
}
impl<G> Blueprint<G>
where
G: Send + Sync + 'static,
{
pub fn load_css(&mut self) -> CssLoader<'_, G> {
CssLoader::new(self)
}
}