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}