1// Copyright 2015 Brendan Zabarauskas and the gl-rs developers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use registry::Registry;
16use std::io;
17
18#[allow(missing_copy_implementations)]
19pub struct StructGenerator;
20
21impl super::Generator for StructGenerator {
22 fn write<W>(&self, registry: &Registry, dest: &mut W) -> io::Result<()>
23 where
24 W: io::Write,
25 {
26 try!(write_header(dest));
27 try!(write_type_aliases(registry, dest));
28 try!(write_enums(registry, dest));
29 try!(write_fnptr_struct_def(dest));
30 try!(write_panicking_fns(registry, dest));
31 try!(write_struct(registry, dest));
32 try!(write_impl(registry, dest));
33 Ok(())
34 }
35}
36
37/// Creates a `__gl_imports` module which contains all the external symbols that we need for the
38/// bindings.
39fn write_header<W>(dest: &mut W) -> io::Result<()>
40where
41 W: io::Write,
42{
43 writeln!(
44 dest,
45 r#"
46 mod __gl_imports {{
47 pub use std::mem;
48 pub use std::marker::Send;
49 pub use std::os::raw;
50 }}
51 "#
52 )
53}
54
55/// Creates a `types` module which contains all the type aliases.
56///
57/// See also `generators::gen_types`.
58fn write_type_aliases<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
59where
60 W: io::Write,
61{
62 try!(writeln!(
63 dest,
64 r#"
65 pub mod types {{
66 #![allow(non_camel_case_types, non_snake_case, dead_code, missing_copy_implementations)]
67 "#
68 ));
69
70 try!(super::gen_types(registry.api, dest));
71
72 writeln!(dest, "}}")
73}
74
75/// Creates all the `<enum>` elements at the root of the bindings.
76fn write_enums<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
77where
78 W: io::Write,
79{
80 for enm: &Enum in &registry.enums {
81 try!(super::gen_enum_item(enm, "types::", dest));
82 }
83
84 Ok(())
85}
86
87/// Creates a `FnPtr` structure which contains the store for a single binding.
88fn write_fnptr_struct_def<W>(dest: &mut W) -> io::Result<()>
89where
90 W: io::Write,
91{
92 writeln!(
93 dest,
94 "
95 #[allow(dead_code, missing_copy_implementations)]
96 #[derive(Clone)]
97 pub struct FnPtr {{
98 /// The function pointer that will be used when calling the function.
99 f: *const __gl_imports::raw::c_void,
100 /// True if the pointer points to a real function, false if points to a `panic!` fn.
101 is_loaded: bool,
102 }}
103
104 impl FnPtr {{
105 /// Creates a `FnPtr` from a load attempt.
106 fn new(ptr: *const __gl_imports::raw::c_void) -> FnPtr {{
107 if ptr.is_null() {{
108 FnPtr {{
109 f: missing_fn_panic as *const __gl_imports::raw::c_void,
110 is_loaded: false
111 }}
112 }} else {{
113 FnPtr {{ f: ptr, is_loaded: true }}
114 }}
115 }}
116
117 /// Returns `true` if the function has been successfully loaded.
118 ///
119 /// If it returns `false`, calling the corresponding function will fail.
120 #[inline]
121 #[allow(dead_code)]
122 pub fn is_loaded(&self) -> bool {{
123 self.is_loaded
124 }}
125 }}
126 "
127 )
128}
129
130/// Creates a `panicking` module which contains one function per GL command.
131///
132/// These functions are the mocks that are called if the real function could not be loaded.
133fn write_panicking_fns<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
134where
135 W: io::Write,
136{
137 writeln!(
138 dest,
139 "#[inline(never)]
140 fn missing_fn_panic() -> ! {{
141 panic!(\"{api} function was not loaded\")
142 }}",
143 api = registry.api
144 )
145}
146
147/// Creates a structure which stores all the `FnPtr` of the bindings.
148///
149/// The name of the struct corresponds to the namespace.
150fn write_struct<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
151where
152 W: io::Write,
153{
154 try!(writeln!(
155 dest,
156 "
157 #[allow(non_camel_case_types, non_snake_case, dead_code)]
158 #[derive(Clone)]
159 pub struct {api} {{",
160 api = super::gen_struct_name(registry.api)
161 ));
162
163 for cmd: &Cmd in &registry.cmds {
164 if let Some(v: &Vec) = registry.aliases.get(&cmd.proto.ident) {
165 try!(writeln!(dest, "/// Fallbacks: {}", v.join(", ")));
166 }
167 try!(writeln!(dest, "pub {name}: FnPtr,", name = cmd.proto.ident));
168 }
169 try!(writeln!(dest, "_priv: ()"));
170
171 writeln!(dest, "}}")
172}
173
174/// Creates the `impl` of the structure created by `write_struct`.
175fn write_impl<W>(registry: &Registry, dest: &mut W) -> io::Result<()>
176where
177 W: io::Write,
178{
179 try!(writeln!(dest,
180 "impl {api} {{
181 /// Load each OpenGL symbol using a custom load function. This allows for the
182 /// use of functions like `glfwGetProcAddress` or `SDL_GL_GetProcAddress`.
183 ///
184 /// ~~~ignore
185 /// let gl = Gl::load_with(|s| glfw.get_proc_address(s));
186 /// ~~~
187 #[allow(dead_code, unused_variables)]
188 pub fn load_with<F>(mut loadfn: F) -> {api} where F: FnMut(&'static str) -> *const __gl_imports::raw::c_void {{
189 #[inline(never)]
190 fn do_metaloadfn(loadfn: &mut dyn FnMut(&'static str) -> *const __gl_imports::raw::c_void,
191 symbol: &'static str,
192 symbols: &[&'static str])
193 -> *const __gl_imports::raw::c_void {{
194 let mut ptr = loadfn(symbol);
195 if ptr.is_null() {{
196 for &sym in symbols {{
197 ptr = loadfn(sym);
198 if !ptr.is_null() {{ break; }}
199 }}
200 }}
201 ptr
202 }}
203 let mut metaloadfn = |symbol: &'static str, symbols: &[&'static str]| {{
204 do_metaloadfn(&mut loadfn, symbol, symbols)
205 }};
206 {api} {{",
207 api = super::gen_struct_name(registry.api)));
208
209 for cmd in &registry.cmds {
210 try!(writeln!(
211 dest,
212 "{name}: FnPtr::new(metaloadfn(\"{symbol}\", &[{fallbacks}])),",
213 name = cmd.proto.ident,
214 symbol = super::gen_symbol_name(registry.api, &cmd.proto.ident),
215 fallbacks = match registry.aliases.get(&cmd.proto.ident) {
216 Some(fbs) => fbs
217 .iter()
218 .map(|name| format!("\"{}\"", super::gen_symbol_name(registry.api, &name)))
219 .collect::<Vec<_>>()
220 .join(", "),
221 None => format!(""),
222 },
223 ))
224 }
225
226 try!(writeln!(dest, "_priv: ()"));
227
228 try!(writeln!(
229 dest,
230 "}}
231 }}"
232 ));
233
234 for cmd in &registry.cmds {
235 try!(writeln!(dest,
236 "#[allow(non_snake_case, unused_variables, dead_code)]
237 #[inline] pub unsafe fn {name}(&self, {params}) -> {return_suffix} {{ \
238 __gl_imports::mem::transmute::<_, extern \"system\" fn({typed_params}) -> {return_suffix}>\
239 (self.{name}.f)({idents}) \
240 }}",
241 name = cmd.proto.ident,
242 params = super::gen_parameters(cmd, true, true).join(", "),
243 typed_params = super::gen_parameters(cmd, false, true).join(", "),
244 return_suffix = cmd.proto.ty,
245 idents = super::gen_parameters(cmd, true, false).join(", "),
246 ))
247 }
248
249 writeln!(
250 dest,
251 "}}
252
253 unsafe impl __gl_imports::Send for {api} {{}}",
254 api = super::gen_struct_name(registry.api)
255 )
256}
257