resticular 0.1.12

Blazingly Fast Static Site Generator
Documentation
use super::{
    config::Config,
    fs::{
        reader::{FileHolder, Reader},
        Data,
    },
    IntoInner,
};
use crate::error::Error;
use crate::{alert_cli, core::fs::Html};
use axum::http::uri::PathAndQuery;
use colored::Colorize;
use fs_extra::file;
use lol_html::{element, HtmlRewriter, Settings};
use scraper::Selector;
use soup::NodeExt;
use soup::{QueryBuilderExt, Soup};
use std::collections::HashMap;
use std::{cell::RefCell, fmt::format};
use std::{fs::read_dir, path::PathBuf};
use tera::{Context, Tera};
pub struct Component {
    pub name: String,
    pub value: String,
    pub path: PathBuf,
}

struct ComponentReader;

impl ComponentReader {
    fn read(path: &str, data: &mut Vec<Component>) -> Result<(), Error> {
        let components = match read_dir(path){
            Ok(dir) => {
                dir.map(|f| f.unwrap())
                .map(|f| f.path())
                .collect::<Vec<_>>()
            },
            Err(_) => return Err(
                Error::EssentialFolderNotExist("`components` folder is an essential folder, it should exist even if its empty or will be kept empty".to_string())
            )

        };

        for path in &components {
            match path.is_dir() {
                false => {
                    let file_name = path.file_name().unwrap().to_str().unwrap();
                    let path_ext = path.extension().unwrap().to_str().unwrap();
                    match path_ext {
                        "md" | "png" | "jpeg" => continue,
                        _ => {
                            let file_data = Reader::reader_out(path.to_path_buf())?;

                            let tag =
                                scraper::Html::parse_fragment(&file_data.into_inner().into_inner());
                            let selector = Selector::parse("restic-component").unwrap();
                            let mut name = String::new();
                            for element in tag.select(&selector) {
                                name = element.value().attr("name").unwrap().to_owned();
                            }
                            let c = Component {
                                name,
                                value: file_data.into_inner().into_inner(),
                                path: path.clone(),
                            };
                            data.push(c);
                        }
                    }
                }
                true => {
                    ComponentReader::read(path.to_str().unwrap(), data).unwrap();
                }
            }
        }

        Ok(())
    }
}

impl Component {
    pub fn read() -> Result<Vec<Component>, Error> {
        let mut data = vec![];
        let config = Config::read_config()?;
        ComponentReader::read(&format!("{}/components", config.source), &mut data)?;
        Ok(data)
    }
    pub fn name(&self) -> String {
        self.name.clone()
    }

    pub fn path(&self) -> PathBuf {
        self.path.clone()
    }

    pub fn data(&self) -> String {
        self.value.clone()
    }

    fn props_data(data: &mut Context, component: &Component, page: &FileHolder<Data<Html>>) {
        let fragment = scraper::Html::parse_fragment(&page.content.into_inner().into_inner());
        let component_selector = Selector::parse(&component.name()).unwrap();

        for element in fragment.select(&component_selector) {
            let elements = element.value().attrs().collect::<Vec<_>>();
            for (attr, value) in elements {
                if attr.starts_with('$') {
                    data.insert(attr.replace("$", "").to_owned(), &value.to_owned());
                } else {
                    continue;
                }
            }
        }
    }
    fn get_props_data(
        component: &Component,
        page: &FileHolder<Data<Html>>,
    ) -> HashMap<String, String> {
        let mut data = HashMap::new();
        let fragment = scraper::Html::parse_fragment(&page.content.into_inner().into_inner());
        let component_selector = Selector::parse(&component.name()).unwrap();

        for element in fragment.select(&component_selector) {
            let elements = element.value().attrs().collect::<Vec<_>>();
            for (attr, value) in elements {
                if attr.starts_with('$') {
                    data.insert(attr.replace("$", "").to_owned(), value.to_owned());
                } else {
                    continue;
                }
            }
        }
        data
    }

    pub fn replace(
        components: Vec<Component>,
        pages: &Vec<FileHolder<Data<Html>>>,
    ) -> Result<Vec<FileHolder<Data<Html>>>, Error> {
        let mut oc = vec![];

        for page in pages {
            let mut output = vec![];
            let mut context = Context::new();
            let mut ele = vec![];
            for component in &components {
                ele.push(element!(&component.name(), |el| {
                    el.append(&component.data(), lol_html::html_content::ContentType::Html);
                    Ok(())
                }));
                let data = Component::get_props_data(component, page);
                data.iter().for_each(|d| {
                    if !component.value.contains(&format!("{{ {} }}", d.0)) {
                        alert_cli!(
                            format!(
                                "\u{26a0} | {} unused prop at {} which is declared in {} component",
                                d.0,
                                page.file_name,
                                component.name()
                            )
                            .bold(),
                            red
                        );
                    }
                });
                Component::props_data(&mut context, component, page);
            }

            let mut rewriter = HtmlRewriter::new(
                Settings {
                    element_content_handlers: ele,
                    ..Settings::default()
                },
                |c: &[u8]| output.extend_from_slice(c),
            );

            rewriter.write(page.content.into_inner().into_inner().as_bytes())?;
            let mut tera = Tera::default();
            tera.add_raw_template(&page.file_name, &String::from_utf8(output.clone()).unwrap())?;
            let ou = tera.render(&page.file_name, &context)?;
            let page_clone = page.clone();
            let holder = FileHolder::new(
                page_clone.path,
                Data::new(Html::new(&ou)),
                page_clone.ext,
                page_clone.file_name,
                page_clone.data,
            );
            oc.push(holder);
        }

        Ok(oc)
    }
}