1#![allow(dead_code)]
8
9extern crate proc_macro;
10extern crate regex;
11
12use proc_macro::{TokenTree, TokenStream, LexError};
13use std::fs::File;
14use std::path::Path;
15use std::io::Read;
16use regex::Regex;
17use std::env::VarError;
18
19#[derive(Debug)]
20enum Error {
21    Unknown(String),
22    StructNameNotFound,
23    Syntax(LexError),
24    InvalidArguments(String),
25    RootDirNotFound(VarError),
26    IOError {
27        file: String,
28        error: std::io::Error,
29    },
30    Multiple(Vec<Error>),
31}
32
33impl ToString for Error {
34    fn to_string(&self) -> String {
35        format!("{:?}", self)
36    }
37}
38
39impl From<LexError> for Error {
40    fn from(error: LexError) -> Self {
41        Error::Syntax(error)
42    }
43}
44
45impl From<VarError> for Error {
46    fn from(error: VarError) -> Self {
47        Error::RootDirNotFound(error)
48    }
49}
50
51impl From<Error> for TokenStream {
52    fn from(error: Error) -> Self {
53        format!("compile_error!(r####\"{:?}\"####);", error).parse().unwrap()
54    }
55}
56
57#[derive(Clone, Debug)]
58struct Field {
59    name: String,
60    type_name: String,
61}
62
63#[derive(Clone, Debug)]
64struct Struct {
65    pub name: String,
66    pub fields: Vec<Field>,
67}
68
69fn parse_field<I>(iter: &mut I) -> Option<Field>
70where
71    I : Iterator<Item = TokenTree> + Clone,
72{
73    let field_name = iter.take_while(|v| match v {
74        TokenTree::Punct(punct) => punct.to_string() != ":",
75        _ => true,
76    }).last().map(|v| match v {
77        TokenTree::Ident(ident) => Some(ident.to_string()),
78        _ => None,
79    })??;
80
81    let type_name = iter.take_while(|v| match v {
82        TokenTree::Punct(punct) => punct.to_string() != ",",
83        _ => true,
84    }).map(|v| v.to_string()).collect::<Vec<_>>().join("");
85
86    if type_name != "" {
87        Some(Field {
88            name: field_name,
89            type_name: type_name,
90        })
91    } else {
92        None
93    }
94}
95
96fn parse_struct<I>(iter: &mut I) -> Result<Struct, Error>
97where
98    I : Iterator<Item = TokenTree> + Clone,
99{
100    let name = match iter.skip_while(|item| {
101        match item {
102            TokenTree::Ident(ident) => ident.to_string() != "struct",
103            _ => true
104        }
105    }).skip(1).next().ok_or(Error::StructNameNotFound)? {
106        TokenTree::Ident(ident) => Ok(ident.to_string()),
107        _ => Err(Error::StructNameNotFound)
108    }?;
109
110    let fields = iter.last().map(|group| {
111        match group {
112            TokenTree::Group(group) => Some(group.stream().into_iter()),
113            _ => None,
114        }
115    }).flatten().map(|mut iter| {
116        let mut fields: Vec<Field> = Default::default();
117
118        while let Some(field) = parse_field(&mut iter) {
119            fields.push(field)
120        }
121
122        fields
123    }).unwrap_or_else(|| Default::default());
124
125    Ok(Struct { name, fields })
126}
127
128fn uniforms_impl(tokens: TokenStream) -> Result<TokenStream, Error> {
129    let parsed = parse_struct(&mut tokens.into_iter())?;
130    let source = format!(
131        r####"impl webgl_rc::uniforms::Uniforms for {struct_name} {{
132            fn uniforms(&self) -> Vec<webgl_rc::uniforms::Field> {{
133                use webgl_rc::uniforms::IntoUniform;
134                vec![
135                    {content}
136                ]
137            }}
138        }}"####,
139        struct_name = parsed.name,
140        content = &parsed.fields.iter().map(|field| {
141            format!(
142                r###"webgl_rc::uniforms::Field {{ name: r#"u_{name}"#, value: self.{name}.into_uniform() }},"###,
143                name = field.name,
144            )
145        }).collect::<Vec<_>>().join("")
146    );
147    Ok(source.parse()?)
148}
149
150#[proc_macro_derive(Uniforms)]
151pub fn uniforms(tokens: TokenStream) -> TokenStream {
152    uniforms_impl(tokens).unwrap_or_else(|error| error.into())
153}
154
155fn attributes_impl(prefix: &str, tokens: TokenStream) -> Result<TokenStream, Error> {
156    let parsed = parse_struct(&mut tokens.into_iter())?;
157    let source = format!(
158        r####"
159            impl webgl_rc::data_buffer::Item for {struct_name} {{
160                fn layout() -> Vec<webgl_rc::data_buffer::Layout> {{
161                    use webgl_rc::types::TypeMark;
162                    vec![
163                        {layout_items}
164                    ]
165                }}
166            }}
167            impl webgl_rc::data_buffer::Writable for {struct_name} {{
168                fn write(&self, output: &mut Vec<f32>) {{
169                    use webgl_rc::data_buffer::Writable;
170                    {write_items}
171                }}
172                fn stride() -> usize {{
173                    use webgl_rc::data_buffer::Writable;
174                    {stride_items}
175                }}
176            }}
177        "####,
178        struct_name = parsed.name,
179        layout_items = &parsed.fields.iter().map(|field| {
180            format!(
181                r###"webgl_rc::data_buffer::Layout {{ name: r#"{prefix}_{name}"#, data_type: <{type_name} as TypeMark>::data_type() }},"###,
182                prefix = prefix,
183                name = field.name,
184                type_name = field.type_name,
185            )
186        }).collect::<Vec<_>>().join(""),
187        write_items = &parsed.fields.iter().map(|field| {
188            format!(
189                r###"self.{name}.write(output);"###,
190                name = field.name,
191            )
192        }).collect::<Vec<_>>().join(""),
193        stride_items = &parsed.fields.iter().map(|field| {
194            format!(
195                r###"<{type_name} as Writable>::stride()"###,
196                type_name = field.type_name,
197            )
198        }).collect::<Vec<_>>().join(" + "),
199    );
200    source.parse().map_err(|error: LexError| error.into())
201}
202
203#[proc_macro_derive(Attributes)]
204pub fn attributes(tokens: TokenStream) -> TokenStream {
205    attributes_impl("a", tokens).unwrap_or_else(|error| error.into())
206}
207
208#[proc_macro_derive(Instances)]
209pub fn instances(tokens: TokenStream) -> TokenStream {
210    attributes_impl("i", tokens).unwrap_or_else(|error| error.into())
211}
212
213struct Content {
214    content: String,
215    dependencies: Vec<String>,
216}
217
218fn load_glsl_file(root: &Path, file: &Path) -> Result<Content, Error> {
219    let mut handle = File::open(file).map_err(|error| {
220        Error::IOError {
221            file: file.to_str().unwrap().into(),
222            error,
223        }
224    })?;
225    let mut source: String = Default::default();
226    handle.read_to_string(&mut source).map_err(|error| {
227        Error::IOError {
228            file: file.to_str().unwrap().into(),
229            error,
230        }
231    })?;
232
233    let mut dependencies = vec![file.to_str().unwrap().into()];
234    let mut errors = Vec::new();
235
236    let source_with_includes = Regex::new(r#"#include\s*(<.+?>|".+?")"#)
237        .unwrap()
238        .replace_all(&source, &mut |captures: ®ex::Captures<'_>| {
239            let capture = captures.get(1).unwrap().as_str();
240            let file_name = if capture.starts_with("<") {
241                root.join(capture.get(1..(capture.len() - 1)).unwrap())
242            } else {
243                file.parent().unwrap().join(capture.get(1..(capture.len() - 1)).unwrap())
244            };
245
246            match load_glsl_file(root, &file_name) {
247                Ok(content) => {
248                    for file in content.dependencies {
249                        dependencies.push(file);
250                    }
251                    content.content
252                }
253                Err(error) => {
254                    errors.push(error);
255                    format!("#error Failed to include file {:?}\n", file_name)
256                }
257            }
258        });
259
260    if errors.is_empty() {
261        Ok(Content {
262            content: source_with_includes.into(),
263            dependencies,
264        })
265    } else {
266        Err(Error::Multiple(errors))
267    }
268}
269
270fn load_glsl_impl(stream: TokenStream) -> Result<TokenStream, Error> {
271    let tokens = stream.into_iter().collect::<Vec<_>>();
272    return if tokens.is_empty() {
273        Err(Error::InvalidArguments("File name not provided".into()))
274    } else if tokens.len() > 1 {
275        Err(Error::InvalidArguments("Too many arguments".into()))
276    } else {
277        let name = match tokens.first().unwrap() {
278            TokenTree::Literal(value) => {
279                Ok(
280                    value.to_string().chars().into_iter()
281                        .skip_while(|c| *c != '"')
282                        .skip(1)
283                        .take_while(|c| *c != '"')
284                        .map(|c| c.to_string())
285                        .collect::<Vec<_>>()
286                        .join("")
287                )
288            },
289            other => Err(Error::Unknown(format!("File name should be a string but {:?} provided", other)))
290        }?;
291
292        let root = Path::new(
293            &std::env::var("CARGO_MANIFEST_DIR")?
294        ).join("glsl");
295
296        let content = load_glsl_file(root.as_path(), root.join(name).as_path())?;
297
298        Ok(format!(
299            r#####"{{ {dependencies}; r####"{content}"#### }}"#####,
300            dependencies = content.dependencies.into_iter().map(|file| {
301                format!(r##"const _: &[u8] = include_bytes!(r#"{file}"#);"##, file = file)
302            }).collect::<Vec<_>>().join(""),
303            content = content.content
304        ).parse()?)
305    }
306}
307
308#[proc_macro]
314pub fn load_glsl(tokens: TokenStream) -> TokenStream {
315    load_glsl_impl(tokens).unwrap_or_else(|error| error.into())
316}