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
use std::collections::HashMap;

use toml::de::Error;
use serde_derive::Deserialize;
use indexmap::{indexmap, IndexMap};

#[cfg(test)]
mod tests {
    use std::fs;
    use std::fs::{File, OpenOptions};
    use std::io::Write;
    use std::path::Path;

    use crate::compile_toml;

    #[test]
    fn it_works() {
        let toml = fs::read_to_string("example/textured.toml").unwrap();
        let (vertex,fragment, config) = compile_toml(toml.as_str()).unwrap();
        println!("Vertex {}",vertex);
        println!("Fragment {}",fragment);

        let out_file = File::create(Path::new("example/textured.vert"));
        out_file.unwrap().write_all(vertex.as_bytes()).expect("write to file");

        let out_file = File::create(Path::new("example/textured.frag"));
        out_file.unwrap().write_all(fragment.as_bytes()).expect("write to file");
    }
}

pub fn compile_toml(config: &str) -> Result<(String, String, Config), Error> {
    let result = toml::from_str(config);
    match result {
        Ok(config) => {
            Ok((compile_vertex(&config), compile_fragment(&config), config))
        }
        Err(result) => Err(result)
    }
}

pub fn compile_vertex(config: &Config) -> String {
    let mut builder: String = String::new();
    builder.push_str(format!("#version {} {}\n", &config.version, &config.profile).as_str());
    for (index, (name, type_)) in config.layout.iter().enumerate() {
        builder.push_str(
            format!("layout (location = {}) in {} {}; \n", index, type_, name).as_str());
    }
    builder.push('\n');
    for (name, type_) in &config.uniform {
        builder.push_str(
            format!("uniform {} {}; \n", type_, name).as_str());
    }
    builder.push('\n');
    for (name, _type) in &config.vertex.output {
        builder.push_str(format!("out {} {}; \n", _type, name).as_str());
    }
    builder.push('\n');
    builder.push_str("void main() {\n \n");
    builder.push_str(config.vertex.source.as_str());
    builder.push_str("\n}\n");

    builder
}

pub fn compile_fragment(config: &Config) -> String {
    let mut builder: String = String::new();
    builder.push_str(format!("#version {} {}\n", &config.version, &config.profile).as_str());

    for (name, type_) in &config.uniform {
        builder.push_str(
            format!("uniform {} {}; \n", type_, name).as_str());
    }
    builder.push('\n');
    for (name, _type) in &config.vertex.output {
        builder.push_str(format!("in {} {}; \n", _type, name).as_str());
    }
    builder.push('\n');
    for (name, _type) in &config.fragment.output {
        builder.push_str(format!("out {} {}; \n", _type, name).as_str());
    }
    builder.push('\n');
    builder.push_str("void main() {\n \n");
    builder.push_str(config.fragment.source.as_str());
    builder.push_str("\n}\n");

    builder
}

#[derive(Deserialize, Debug)]
pub struct Config {
    version: u32,
    profile: String,
    layout: IndexMap<String, String>,
    uniform: IndexMap<String, String>,
    vertex: ShaderConfig,
    fragment: ShaderConfig,
}

#[derive(Deserialize, Debug)]
pub struct ShaderConfig {
    output: IndexMap<String, String>,
    source: String,
}