dsa_lib/
lib.rs

1use indexmap::IndexMap;
2use serde_derive::Deserialize;
3use toml::de::Error;
4use toml::Value;
5
6#[cfg(test)]
7mod tests {
8    use std::fs;
9    use std::fs::File;
10    use std::io::Write;
11    use std::path::Path;
12
13    use crate::compile_toml;
14
15    #[test]
16    fn it_works() {
17        let toml = fs::read_to_string("example/textured.toml").unwrap();
18        let (vertex, fragment, _config) = compile_toml(toml.as_str()).unwrap();
19        println!("Vertex {}", vertex);
20        println!("Fragment {}", fragment);
21
22        let out_file = File::create(Path::new("example/textured.vert"));
23        out_file.unwrap().write_all(vertex.as_bytes()).expect("write to file");
24
25        let out_file = File::create(Path::new("example/textured.frag"));
26        out_file.unwrap().write_all(fragment.as_bytes()).expect("write to file");
27    }
28}
29
30pub fn compile_toml(config: &str) -> Result<(String, String, Config), Error> {
31    let result = toml::from_str(config);
32    match result {
33        Ok(config) => {
34            Ok((compile_vertex(&config), compile_fragment(&config), config))
35        }
36        Err(result) => Err(result)
37    }
38}
39
40pub fn compile_vertex(config: &Config) -> String {
41    let mut builder: String = String::new();
42    builder.push_str(format!("#version {} {}\n", &config.version, &config.profile).as_str());
43    for (index, (name, type_)) in config.layout.iter().enumerate() {
44        builder.push_str(
45            format!("layout (location = {}) in {} {}; \n", index, type_, name).as_str());
46    }
47    builder.push('\n');
48    for (name, type_) in &config.uniform {
49        builder.push_str(
50            format!("uniform {} {}; \n", type_, name).as_str());
51    }
52    builder.push('\n');
53    for (name, _type) in &config.vertex.output {
54        builder.push_str(format!("out {} {}; \n", _type, name).as_str());
55    }
56    builder.push('\n');
57    if let Some(constants) = &config.vertex.constants.as_ref() {
58        for (name, value) in constants.iter() {
59            if let Value::String(str) = value {
60                builder.push_str(format!("#define {}{} \n", name, str).as_str());
61            } else {
62                builder.push_str(format!("#define {} {} \n", name, value).as_str());
63            }
64        }
65    }
66
67    builder.push('\n');
68    builder.push_str("void main() {\n \n");
69    builder.push_str(config.vertex.source.as_str());
70    builder.push_str("\n}\n");
71
72    builder.push('\n');
73    if let Some(constants) = &config.vertex.functions.as_ref() {
74        for (_, value) in constants.iter() {
75            builder.push_str(format!("{} \n\n", value).as_str());
76        }
77    }
78
79    builder
80}
81
82pub fn compile_fragment(config: &Config) -> String {
83    let mut builder: String = String::new();
84    builder.push_str(format!("#version {} {}\n", &config.version, &config.profile).as_str());
85
86    for (name, type_) in &config.uniform {
87        builder.push_str(
88            format!("uniform {} {}; \n", type_, name).as_str());
89    }
90    builder.push('\n');
91    for (name, _type) in &config.vertex.output {
92        builder.push_str(format!("in {} {}; \n", _type, name).as_str());
93    }
94    builder.push('\n');
95    for (name, _type) in &config.fragment.output {
96        builder.push_str(format!("out {} {}; \n", _type, name).as_str());
97    }
98    builder.push('\n');
99    if let Some(constants) = &config.fragment.constants.as_ref() {
100        for (name, value) in constants.iter() {
101            if let Value::String(str) = value {
102                builder.push_str(format!("#define {}{} \n", name, str).as_str());
103            } else {
104                builder.push_str(format!("#define {} {} \n", name, value).as_str());
105            }
106        }
107    }
108
109    builder.push('\n');
110    builder.push_str("void main() {\n \n");
111    builder.push_str(config.fragment.source.as_str());
112    builder.push_str("\n}\n");
113
114    builder.push('\n');
115    if let Some(constants) = &config.fragment.functions.as_ref() {
116        for (_, value) in constants.iter() {
117            builder.push_str(format!("{} \n\n", value).as_str());
118        }
119    }
120
121    builder
122}
123
124#[derive(Deserialize, Debug)]
125pub struct Config {
126    pub version: u32,
127    pub profile: String,
128    pub layout: IndexMap<String, String>,
129    pub uniform: IndexMap<String, String>,
130    pub vertex: ShaderConfig,
131    pub fragment: ShaderConfig,
132}
133
134#[derive(Deserialize, Debug)]
135pub struct ShaderConfig {
136    pub constants: Option<IndexMap<String, Value>>,
137    pub functions: Option<IndexMap<String, String>>,
138    pub output: IndexMap<String, String>,
139    pub source: String,
140}