1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
use std::{
    fs::{self, read_to_string},
    path::Path,
};

use lightningcss::{
    stylesheet::{ParserOptions, PrinterOptions, StyleSheet},
    visitor::Visit,
};

use crate::{
    config::{read_config, Config},
    visit_class::check_html,
    visit_selectors::ClassVisitor,
};

pub fn start_all() {
    let config = read_config();
    match config {
        Ok(config) => {
            let validate = config.validate();
            if config.validate().is_ok() {
                let mut css_walker = CSSWalker::default();

                for dir in &config.css_dir {
                    css_walker.walk_tree(dir, &config);
                }

                let mut html_walker =
                    HTMLWalker::new(&css_walker.class_visitor);

                for dir in &config.html_dir {
                    html_walker.walk_tree(dir, &config);
                }
                if let Some(assets_dir) = &config.assets_dir {
                    for dir in assets_dir {
                        html_walker.walk_tree(dir, &config);
                    }
                }
            } else {
                println!("{}", validate.err().unwrap());
            }
        }
        Err(e) => {
            println!("{}", e);
        }
    }
}

fn handle_path(path: &Path) -> &Path {
    if path.starts_with("./") {
        if let Some(path2) = path.to_str() {
            let mut path2 = path2.chars();
            path2.next();
            path2.next();
            return Path::new(path2.as_str());
        }
    }
    path
}

trait TreeWalker {
    fn walk(&mut self, old_content: String) -> Option<String>;

    fn write(&self, path: &Path, new_content: Option<&str>, config: &Config) {
        let output_path = Path::new(&config.output_dir);
        let new_path = output_path.join(handle_path(path));
        if let Some(parent) = new_path.parent() {
            dbg!(path);
            let _ = std::fs::create_dir_all(parent);
            if let Some(new_content) = new_content {
                let _ = std::fs::write(new_path, new_content);
            } else {
                let _ = fs::copy(path, new_path);
            }
        }
    }

    fn walk_tree(&mut self, dir: &String, config: &Config) {
        if let Ok(paths) = std::fs::read_dir(dir) {
            for path in paths.flatten() {
                if let Ok(meta) = path.metadata() {
                    if meta.is_file() {
                        if let Ok(old_content) = read_to_string(path.path()) {
                            if let Some(new_content) = self.walk(old_content) {
                                self.write(
                                    &path.path(),
                                    Some(&new_content),
                                    config,
                                );
                            }
                        } else {
                            self.write(&path.path(), None, config)
                        }
                    } else if meta.is_dir() {
                        self.walk_tree(dir, config);
                    }
                }
            }
        }
    }
}

#[derive(Default)]
struct CSSWalker {
    pub class_visitor: ClassVisitor,
}

impl TreeWalker for CSSWalker {
    fn walk(&mut self, old_content: String) -> Option<String> {
        let a = StyleSheet::parse(&old_content, ParserOptions::default());
        if let Ok(mut stylesheet) = a {
            let _ = stylesheet.visit(&mut self.class_visitor);
            let opt = PrinterOptions {
                minify: true,
                ..Default::default()
            };
            if let Ok(f) = stylesheet.to_css(opt) {
                return Some(f.code);
            }
        }
        None
    }
}

struct HTMLWalker<'a> {
    pub class_visitor: &'a ClassVisitor,
}

impl<'a> HTMLWalker<'a> {
    pub fn new(class_visitor: &'a ClassVisitor) -> Self {
        Self { class_visitor }
    }
}

impl<'a> TreeWalker for HTMLWalker<'a> {
    fn walk(&mut self, old_content: String) -> Option<String> {
        if let Ok(html) = check_html(&old_content, self.class_visitor) {
            return Some(html);
        }
        None
    }
}