use {
crate::{
fh::{
Component as FHComponent,
ComponentInformation as FHComponentInformation, FileHierarchy,
Level,
},
LewpError, LewpErrorKind,
},
lewp_css::{
cssparser::ToCss,
domain::{
at_rules::{document::DocumentAtRule, media::MediaAtRule},
selectors::OurSelectorImpl,
CssRule, CssRules, StyleRule,
},
Stylesheet,
},
selectors::parser::Selector,
std::{path::PathBuf, rc::Rc},
};
pub struct Component {
fh: Rc<FileHierarchy>,
component_information: Rc<FHComponentInformation>,
}
impl FHComponent for Component {
type Content = Stylesheet;
type ContentParameter = ();
fn component_information(&self) -> Rc<FHComponentInformation> {
self.component_information.clone()
}
fn content(
&self,
params: Self::ContentParameter,
) -> Result<Self::Content, LewpError> {
let files = self.fh.get_file_list(self)?;
let css_raw = self.combine_files(files)?;
let stylesheet = match Stylesheet::parse(&css_raw) {
Ok(s) => s,
Err(msg) => {
return Err(LewpError::new(
LewpErrorKind::Css,
&format!("{:#?}", msg),
self.component_information.clone(),
));
}
};
match &self.component_information.level {
Level::Page => return Ok(stylesheet), _ => (),
}
let stylesheet = self.isolate_stylesheet(stylesheet)?;
Ok(stylesheet)
}
fn file_hierarchy(&self) -> Rc<FileHierarchy> {
self.fh.clone()
}
}
impl Component {
pub fn new(
component_information: Rc<FHComponentInformation>,
fh: Rc<FileHierarchy>,
) -> Self {
Self {
fh,
component_information,
}
}
fn combine_files(
&self,
css_files: Vec<PathBuf>,
) -> Result<String, LewpError> {
let mut css_combined = String::new();
for css_file_name in css_files {
let css = match std::fs::read_to_string(&css_file_name) {
Ok(r) => r,
Err(msg) => {
return Err(LewpError::new(
LewpErrorKind::Css,
&format!("Error reading stylesheet file: {}", msg),
self.component_information.clone(),
));
}
};
css_combined.push_str(&css);
}
Ok(css_combined)
}
fn isolate_stylesheet(
&self,
stylesheet: Stylesheet,
) -> Result<<Self as FHComponent>::Content, LewpError> {
let mut stylesheet = stylesheet;
self.isolate_rules(&mut stylesheet.rules, true)?;
Ok(stylesheet)
}
fn isolate_rules(
&self,
rules: &mut CssRules,
recursive: bool,
) -> Result<(), LewpError> {
for rule in &mut rules.0 {
match rule {
CssRule::Style(StyleRule { selectors, .. }) => {
for s in &mut selectors.0 {
self.add_module_prefix(s)?;
}
}
CssRule::Media(MediaAtRule { rules, .. })
| CssRule::Document(DocumentAtRule { rules, .. }) => {
if !recursive {
continue;
}
self.isolate_rules(rules, true)?
}
_ => {}
}
}
Ok(())
}
fn add_module_prefix(
&self,
selector: &mut Selector<OurSelectorImpl>,
) -> Result<(), LewpError> {
let mut old = String::new();
if let Err(e) = selector.to_css(&mut old) {
return Err(LewpError::new(
LewpErrorKind::Css,
&format!("{:#?}", e),
self.component_information().clone(),
));
};
let new = match lewp_css::parse_css_selector(&format!(
".{} {}",
self.id(),
old
)) {
Err(e) => {
return Err(LewpError::new(
LewpErrorKind::Css,
&format!("{:#?}", e),
self.component_information().clone(),
));
}
Ok(s) => s,
};
*selector = new;
Ok(())
}
}