resticular 0.1.2

Blazingly Fast Static Site Generator
Documentation
use super::{Content, Data, Html, Markdown};
use crate::core::config::Config;
use crate::core::markdown::MarkdownParser;
use crate::core::IntoInner;
use crate::error::Error;
use minifier::css;
use minifier::js;
use std::fmt::Debug;
use std::fs::create_dir;
use std::fs::create_dir_all;
use std::fs::File;
use std::fs::{read_dir, remove_file};
use std::io::{BufReader, BufWriter, Read, Write};
use std::path::PathBuf;
use tracing::info;

#[derive(Debug, Clone)]
pub struct FileContent(String);
impl IntoInner for FileContent {
    type Output = String;

    fn into_inner(&self) -> Self::Output {
        self.0.to_owned()
    }
}
#[derive(Debug, Clone)]
pub struct FileHolder<T> {
    pub path: PathBuf,
    pub content: T,
    pub ext: String,
    pub file_name: String,
}

impl PartialEq for FileHolder<Data<Html>> {
    fn eq(&self, other: &Self) -> bool {
        self.path == other.path
            && self.content == other.content
            && self.ext == other.ext
            && self.file_name == other.file_name
    }
}
impl<T> FileHolder<T> {
    pub fn new(path: PathBuf, content: T, ext: String, file_name: String) -> Self {
        Self {
            path,
            content,
            ext,
            file_name,
        }
    }
}

impl<T: 'static + Debug> Content for FileHolder<T> {}
impl FileContent {
    pub fn new(content: String) -> Self {
        Self(content)
    }
}
impl From<FileContent> for Html {
    fn from(d: FileContent) -> Self {
        Self(d.0)
    }
}
impl From<FileContent> for Markdown {
    fn from(d: FileContent) -> Self {
        Self(d.0)
    }
}

impl From<Data<FileContent>> for Data<Html> {
    fn from(d: Data<FileContent>) -> Self {
        Self {
            file_content: d.into_inner().into(),
        }
    }
}

impl From<Data<FileContent>> for Data<Markdown> {
    fn from(d: Data<FileContent>) -> Self {
        Self {
            file_content: d.into_inner().into(),
        }
    }
}

pub struct FolderBuilder;

impl FolderBuilder {
    pub fn check_build_dir() -> bool {
        let config = Config::read_config().unwrap();
        let dir = read_dir(config.out_dir);
        match dir {
            Ok(_) => true,
            Err(_) => false,
        }
    }
    pub fn create_folder() -> Result<(), Error> {
        if FolderBuilder::check_build_dir() {
            let config = Config::read_config()?;
            let dir_content = read_dir(config.out_dir)?.collect::<Vec<_>>();
            for file in dir_content {
                let file = file?.path();
                remove_file(file)?;
            }

            return Ok(());
        }
        let config = Config::read_config()?;
        info!("Creating {}.", &config.out_dir);
        create_dir(PathBuf::from(config.out_dir))?;
        Ok(())
    }

    pub fn start_creating_files(pages: &Vec<FileHolder<Data<Html>>>) -> Result<(), Error> {
        let config = Config::read_config()?;
        for page in pages {
            let _ = page
                .path
                .to_str()
                .unwrap()
                .replace(&config.dir, &config.out_dir);
            info!("Creating {}.", &page.file_name);
            Writer::write(
                PathBuf::from(format!("{}/{}", config.out_dir, page.file_name)),
                page.content.clone(),
            )?;
        }

        let other_file = Reader::new(config.clone().dir.into()).read_other()?;
        for files in other_file {
            let _ = files
                .path
                .to_str()
                .unwrap()
                .replace(&config.dir, &config.out_dir);
            info!("Creating {}.", &files.file_name);
            Writer::write(
                PathBuf::from(format!("{}/{}", config.out_dir, files.file_name)),
                files.content.clone(),
            )?;
        }

        Ok(())
    }
}

#[derive(Debug, Clone)]
pub struct Writer;
impl Writer {
    pub fn write<T: IntoInner<Output = String> + Clone>(
        path: PathBuf,
        content: Data<T>,
    ) -> Result<(), Error> {
        info!("Writing {}.", path.to_str().unwrap());
        let content = content.into_inner().into_inner();
        let file = File::create(path)?;
        let mut writer = BufWriter::new(file);
        writer.write(content.as_bytes())?;
        Ok(())
    }
}

#[derive(Debug, Clone)]
pub struct Reader {
    pub path: PathBuf,
}

impl Reader {
    pub fn new(path: PathBuf) -> Self {
        Self { path }
    }

    pub fn read_other(&self) -> Result<Vec<FileHolder<Data<FileContent>>>, Error> {
        let config = Config::read_config()?;
        let mut data = vec![];
        read_push_other_files(&config.dir.into(), &mut data)?;
        Ok(data)
    }

    pub fn reader(&self) -> Result<Data<FileContent>, Error> {
        let mut buffer = String::new();
        let f = File::open(&self.path)?;
        let mut reader = BufReader::new(f);
        reader.read_to_string(&mut buffer)?;
        Ok(Data::new(FileContent::new(buffer)))
    }

    pub fn reader_out(path: PathBuf) -> Result<Data<FileContent>, Error> {
        let mut buffer = String::new();
        let f = File::open(path)?;
        let mut reader = BufReader::new(f);
        reader.read_to_string(&mut buffer)?;
        Ok(Data::new(FileContent::new(buffer)))
    }
    pub fn read_dir_files(&self) -> Result<Vec<Box<dyn Content>>, Error> {
        let mut data: Vec<Box<dyn Content>> = Vec::new();
        let dir_data = std::fs::read_dir(&self.path)?
            .map(|f| f.unwrap())
            .map(|f| f.path())
            .collect::<Vec<_>>();
        for path in &dir_data {
            let file_name = path.to_str().unwrap().split('.').collect::<Vec<_>>()[0];
            let path_ext = path.extension().unwrap().to_str().unwrap();
            match path_ext {
                "html" => {
                    let file_data: Data<Html> = Reader::reader_out(path.to_path_buf())?.into();
                    let file_holder = FileHolder::new(
                        path.clone(),
                        file_data,
                        "html".to_owned(),
                        file_name.to_string(),
                    );
                    data.push(Box::new(file_holder));
                }
                "md" => {
                    let file_data: Data<Markdown> = Reader::reader_out(path.to_path_buf())?.into();
                    let file_holder = FileHolder::new(
                        path.clone(),
                        file_data,
                        "md".to_owned(),
                        file_name.to_string(),
                    );
                    data.push(Box::new(file_holder));
                }
                _ => continue,
            }
        }
        Ok(data)
    }

    fn find_files(
        path: &PathBuf,
        file_name: &str,
        ext: &str,
    ) -> Result<FileHolder<Data<Html>>, Error> {
        let file_data: Data<Html> = Reader::reader_out(path.to_path_buf())?.into();
        let file_holder = FileHolder::new(
            path.clone(),
            file_data,
            ext.to_owned(),
            file_name.to_string(),
        );
        Ok(file_holder)
    }
}

pub fn start_convert_and_parse(files: Vec<Box<dyn Content>>) -> Vec<FileHolder<Data<Html>>> {
    let mut output = Vec::new();
    for file in files {
        let downcasted = file.downcast_ref::<FileHolder<Data<Markdown>>>();
        match downcasted {
            Some(f) => {
                let file_content = f.clone().content.into_inner();
                let markdown_parser = MarkdownParser::new(Data::new(file_content));
                let f_clone = f.clone();
                output.push(FileHolder::new(
                    f_clone.path,
                    Data::new(markdown_parser.convert().into_inner()),
                    "md".to_string(),
                    f_clone.file_name,
                ));
            }
            None => {
                let downcasted_html = file.downcast_ref::<FileHolder<Data<Html>>>();
                match downcasted_html {
                    Some(f) => {
                        let file_content = f.clone().content.into_inner();
                        let f_clone = f.clone();
                        output.push(FileHolder::new(
                            f_clone.path,
                            Data::new(file_content),
                            "html".to_string(),
                            f_clone.file_name,
                        ));
                    }
                    None => {
                        panic!("Error")
                    }
                }
            }
        }
    }
    output
}

pub fn read(path: &str) -> Result<Vec<Box<dyn Content>>, Error> {
    let path = PathBuf::from(path);
    let mut data = Vec::new();
    read_push(&path, &mut data)?;
    Ok(data)
}

fn read_push(path: &PathBuf, data: &mut Vec<Box<dyn Content>>) -> Result<(), Error> {
    let dir_data = std::fs::read_dir(&path)?
        .map(|f| f.unwrap())
        .map(|f| f.path())
        .collect::<Vec<_>>();

    for path in &dir_data {
        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 {
                    "html" => {
                        let file_data: Data<Html> = Reader::reader_out(path.to_path_buf())?.into();
                        let file_holder = FileHolder::new(
                            path.clone(),
                            file_data,
                            "html".to_owned(),
                            file_name.to_string(),
                        );
                        data.push(Box::new(file_holder));
                    }
                    "md" => {
                        let file_data: Data<Markdown> =
                            Reader::reader_out(path.to_path_buf())?.into();
                        let file_holder = FileHolder::new(
                            path.clone(),
                            file_data,
                            "md".to_owned(),
                            file_name.to_string(),
                        );
                        data.push(Box::new(file_holder));
                    }
                    _ => continue,
                }
            }
            true => {
                read_push(path, data).unwrap();
            }
        }
    }

    Ok(())
}

fn read_push_other_files(
    path: &PathBuf,
    data: &mut Vec<FileHolder<Data<FileContent>>>,
) -> Result<(), Error> {
    let dir_data = std::fs::read_dir(&path)?
        .map(|f| f.unwrap())
        .map(|f| f.path())
        .collect::<Vec<_>>();

    for path in &dir_data {
        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 {
                    "html" | "md" => continue,
                    _ => {
                        let file_data = Reader::reader_out(path.to_path_buf())?;
                        let file_holder = FileHolder::new(
                            path.clone(),
                            file_data,
                            path_ext.to_owned(),
                            file_name.to_string(),
                        );
                        data.push(file_holder);
                    }
                }
            }
            true => {
                read_push_other_files(path, data).unwrap();
            }
        }
    }

    Ok(())
}

impl IntoInner for String {
    type Output = String;

    fn into_inner(&self) -> Self::Output {
        self.to_owned()
    }
}