Source code

Revision control

Copy as Markdown

Other Tools

// Copyright 2015 Brendan Zabarauskas and the gl-rs developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
extern crate khronos_api;
use std::borrow::Cow;
use std::collections::btree_map::Entry;
use std::collections::{BTreeMap, BTreeSet};
use std::io;
use xml::attribute::OwnedAttribute;
use xml::reader::XmlEvent;
use xml::EventReader as XmlEventReader;
use registry::{Binding, Cmd, Enum, GlxOpcode, Group, Registry};
use {Api, Fallbacks, Profile};
pub fn from_xml<R: io::Read>(
src: R,
filter: &Filter,
require_feature: bool,
) -> Registry {
XmlEventReader::new(src)
.into_iter()
.map(Result::unwrap)
.filter_map(ParseEvent::from_xml)
.parse(filter, require_feature)
}
#[derive(Debug, PartialEq, Eq)]
struct Attribute {
key: String,
value: String,
}
impl Attribute {
fn new<Key, Value>(key: Key, value: Value) -> Attribute
where
Key: ToString,
Value: ToString,
{
Attribute {
key: key.to_string(),
value: value.to_string(),
}
}
}
impl From<OwnedAttribute> for Attribute {
fn from(attribute: OwnedAttribute) -> Attribute {
Attribute::new(attribute.name.local_name, attribute.value)
}
}
#[derive(Debug, PartialEq, Eq)]
enum ParseEvent {
Start(String, Vec<Attribute>),
End(String),
Text(String),
}
impl ParseEvent {
fn from_xml(event: XmlEvent) -> Option<ParseEvent> {
match event {
XmlEvent::StartDocument { .. } => None,
XmlEvent::EndDocument => None,
XmlEvent::StartElement {
name, attributes, ..
} => {
let attributes = attributes.into_iter().map(Attribute::from).collect();
Some(ParseEvent::Start(name.local_name, attributes))
},
XmlEvent::EndElement { name } => Some(ParseEvent::End(name.local_name)),
XmlEvent::Characters(chars) => Some(ParseEvent::Text(chars)),
XmlEvent::ProcessingInstruction { .. } => None,
XmlEvent::CData(_) => None,
XmlEvent::Comment(_) => None,
XmlEvent::Whitespace(_) => None,
}
}
}
fn api_from_str(src: &str) -> Result<Option<Api>, ()> {
match src {
"gl" => Ok(Some(Api::Gl)),
"glx" => Ok(Some(Api::Glx)),
"wgl" => Ok(Some(Api::Wgl)),
"egl" => Ok(Some(Api::Egl)),
"glcore" => Ok(Some(Api::GlCore)),
"gles1" => Ok(Some(Api::Gles1)),
"gles2" => Ok(Some(Api::Gles2)),
"glsc2" => Ok(Some(Api::Glsc2)),
"disabled" => Ok(None),
_ => Err(()),
}
}
fn profile_from_str(src: &str) -> Result<Profile, ()> {
match src {
"core" => Ok(Profile::Core),
"compatibility" => Ok(Profile::Compatibility),
_ => Err(()),
}
}
fn underscore_numeric_prefix(src: &str) -> String {
match src.chars().next() {
Some(c) if c.is_numeric() => format!("_{}", src),
Some(_) | None => src.to_string(),
}
}
fn underscore_keyword(ident: String) -> String {
match ident.as_ref() {
"in" => "in_".to_string(),
"ref" => "ref_".to_string(),
"type" => "type_".to_string(),
_ => ident,
}
}
fn trim_str<'a>(s: &'a str, trim: &str) -> &'a str {
if s.starts_with(trim) {
&s[trim.len()..]
} else {
s
}
}
fn trim_enum_prefix(ident: &str, api: Api) -> String {
let ident = match api {
Api::Gl | Api::GlCore | Api::Gles1 | Api::Gles2 | Api::Glsc2 => trim_str(ident, "GL_"),
Api::Glx => trim_str(ident, "GLX_"),
Api::Wgl => trim_str(ident, "WGL_"),
Api::Egl => trim_str(ident, "EGL_"),
};
underscore_numeric_prefix(ident)
}
fn make_enum(ident: String, ty: Option<String>, value: String, alias: Option<String>) -> Enum {
let (ty, value, cast) = {
if value.starts_with("((") && value.ends_with(")") {
// Some enums have a value of the form `'((' type ')' expr ')'`.
// nothing to see here....
// just brute forcing some paren matching... (ノ ◕ ◡ ◕)ノ *:・゚✧
let working = &value[2..value.len() - 1];
if let Some((i, _)) = working.match_indices(")").next() {
let ty = working[..i].to_string();
let value = working[i + 1..].to_string();
(Cow::Owned(ty), value, true)
} else {
panic!("Unexpected value format: {}", value)
}
} else {
let ty = match ty {
Some(ref ty) if ty == "u" => "GLuint",
Some(ref ty) if ty == "ull" => "GLuint64",
Some(ty) => panic!("Unhandled enum type: {}", ty),
None if value.starts_with("\"") => "&'static str",
None if ident == "TRUE" || ident == "FALSE" => "GLboolean",
None => "GLenum",
};
(Cow::Borrowed(ty), value, false)
}
};
Enum {
ident: ident,
value: value,
cast: cast,
alias: alias,
ty: ty,
}
}
fn make_egl_enum(ident: String, ty: Option<String>, value: String, alias: Option<String>) -> Enum {
let (ty, value, cast) = {
if value.starts_with("EGL_CAST(") && value.ends_with(")") {
// Handling "SpecialNumbers" in the egl.xml file
// The values for these enums has the form `'EGL_CAST(' type ',' expr ')'`.
let working = &value[9..value.len() - 1];
if let Some((i, _)) = working.match_indices(",").next() {
let ty = working[..i].to_string();
let value = working[i + 1..].to_string();
(Cow::Owned(ty), value, true)
} else {
panic!("Unexpected value format: {}", value)
}
} else {
match value.chars().next() {
Some('-') | Some('0'..='9') => (),
_ => panic!("Unexpected value format: {}", value),
}
let ty = match ty {
Some(ref ty) if ty == "ull" => "EGLuint64KHR",
Some(ty) => panic!("Unhandled enum type: {}", ty),
None if value.starts_with('-') => "EGLint",
None if ident == "TRUE" || ident == "FALSE" => "EGLBoolean",
None => "EGLenum",
};
(Cow::Borrowed(ty), value, false)
}
};
Enum {
ident: ident,
value: value,
cast: cast,
alias: alias,
ty: ty,
}
}
fn trim_cmd_prefix(ident: &str, api: Api) -> &str {
match api {
Api::Gl | Api::GlCore | Api::Gles1 | Api::Gles2 | Api::Glsc2 => trim_str(ident, "gl"),
Api::Glx => trim_str(ident, "glX"),
Api::Wgl => trim_str(ident, "wgl"),
Api::Egl => trim_str(ident, "egl"),
}
}
fn merge_map(a: &mut BTreeMap<String, Vec<String>>, b: BTreeMap<String, Vec<String>>) {
for (k, v) in b {
match a.entry(k) {
Entry::Occupied(mut ent) => {
ent.get_mut().extend(v);
},
Entry::Vacant(ent) => {
ent.insert(v);
},
}
}
}
#[derive(Clone)]
struct Feature {
pub api: Api,
pub name: String,
pub number: String,
pub requires: Vec<Require>,
pub removes: Vec<Remove>,
}
#[derive(Clone)]
struct Require {
/// A reference to the earlier types, by name
pub enums: Vec<String>,
/// A reference to the earlier types, by name
pub commands: Vec<String>,
}
#[derive(Clone)]
struct Remove {
// always Core, for now
pub profile: Profile,
/// A reference to the earlier types, by name
pub enums: Vec<String>,
/// A reference to the earlier types, by name
pub commands: Vec<String>,
}
#[derive(Clone)]
struct Extension {
pub name: String,
/// which apis this extension is defined for (see Feature.api)
pub supported: Vec<Api>,
pub requires: Vec<Require>,
}
pub struct Filter {
pub api: Api,
pub fallbacks: Fallbacks,
pub extensions: BTreeSet<String>,
pub profile: Profile,
pub version: String,
}
trait Parse: Sized + Iterator<Item = ParseEvent> {
fn parse(mut self, filter: &Filter, require_feature: bool) -> Registry {
self.consume_start_element("registry");
let mut enums = Vec::new();
let mut cmds = Vec::new();
let mut features = Vec::new();
let mut extensions = Vec::new();
let mut aliases = BTreeMap::new();
let mut groups: BTreeMap<String, Group> = BTreeMap::new();
while let Some(event) = self.next() {
match event {
// ignores
ParseEvent::Text(_) => (),
ParseEvent::Start(ref name, _) if name == "comment" => self.skip_to_end("comment"),
ParseEvent::Start(ref name, _) if name == "types" => self.skip_to_end("types"),
// add group namespace
ParseEvent::Start(ref name, _) if name == "groups" => {
groups.extend(self.consume_groups(filter.api));
},
// add enum namespace
ParseEvent::Start(ref name, ref attributes) if name == "enums" => {
enums.extend(self.consume_enums(filter.api));
let enums_group = get_attribute(&attributes, "group");
let enums_type = get_attribute(&attributes, "type");
if let Some(group) = enums_group.and_then(|name| groups.get_mut(&name)) {
group.enums_type = enums_type;
}
},
// add command namespace
ParseEvent::Start(ref name, _) if name == "commands" => {
let (new_cmds, new_aliases) = self.consume_cmds(filter.api);
cmds.extend(new_cmds);
merge_map(&mut aliases, new_aliases);
},
ParseEvent::Start(ref name, ref attributes) if name == "feature" => {
debug!("Parsing feature: {:?}", attributes);
features.push(Feature::convert(&mut self, &attributes));
},
ParseEvent::Start(ref name, _) if name == "extensions" => loop {
match self.next().unwrap() {
ParseEvent::Start(ref name, ref attributes) if name == "extension" => {
extensions.push(Extension::convert(&mut self, &attributes));
},
ParseEvent::End(ref name) if name == "extensions" => break,
event => panic!("Unexpected message {:?}", event),
}
},
// finished building the registry
ParseEvent::End(ref name) if name == "registry" => break,
// error handling
event => panic!("Expected </registry>, found: {:?}", event),
}
}
let mut desired_enums = BTreeSet::new();
let mut desired_cmds = BTreeSet::new();
// find the features we want
let mut found_feature = false;
for feature in &features {
// XXX: verify that the string comparison with <= actually works as desired
if feature.api == filter.api && feature.number <= filter.version {
for require in &feature.requires {
desired_enums.extend(require.enums.iter().map(|x| x.clone()));
desired_cmds.extend(require.commands.iter().map(|x| x.clone()));
}
for remove in &feature.removes {
if remove.profile == filter.profile {
for enm in &remove.enums {
debug!("Removing {}", enm);
desired_enums.remove(enm);
}
for cmd in &remove.commands {
debug!("Removing {}", cmd);
desired_cmds.remove(cmd);
}
}
}
}
if feature.number == filter.version {
found_feature = true;
}
}
if !found_feature && require_feature {
panic!("Did not find version {} in the registry", filter.version);
}
for extension in &extensions {
if filter.extensions.contains(&extension.name) {
if !extension.supported.contains(&filter.api) {
panic!(
"Requested {}, which doesn't support the {} API",
extension.name, filter.api
);
}
for require in &extension.requires {
desired_enums.extend(require.enums.iter().map(|x| x.clone()));
desired_cmds.extend(require.commands.iter().map(|x| x.clone()));
}
}
}
let is_desired_enum = |e: &Enum| {
desired_enums.contains(&("GL_".to_string() + &e.ident))
|| desired_enums.contains(&("WGL_".to_string() + &e.ident))
|| desired_enums.contains(&("GLX_".to_string() + &e.ident))
|| desired_enums.contains(&("EGL_".to_string() + &e.ident))
};
let is_desired_cmd = |c: &Cmd| {
desired_cmds.contains(&("gl".to_string() + &c.proto.ident))
|| desired_cmds.contains(&("wgl".to_string() + &c.proto.ident))
|| desired_cmds.contains(&("glX".to_string() + &c.proto.ident))
|| desired_cmds.contains(&("egl".to_string() + &c.proto.ident))
};
Registry {
api: filter.api,
enums: enums.into_iter().filter(is_desired_enum).collect(),
cmds: cmds.into_iter().filter(is_desired_cmd).collect(),
aliases: if filter.fallbacks == Fallbacks::None {
BTreeMap::new()
} else {
aliases
},
groups,
}
}
fn consume_characters(&mut self) -> String {
match self.next().unwrap() {
ParseEvent::Text(ch) => ch,
event => panic!("Expected characters, found: {:?}", event),
}
}
fn consume_start_element(&mut self, expected_name: &str) -> Vec<Attribute> {
match self.next().unwrap() {
ParseEvent::Start(name, attributes) => {
if expected_name == name {
attributes
} else {
panic!("Expected <{}>, found: <{}>", expected_name, name)
}
},
event => panic!("Expected <{}>, found: {:?}", expected_name, event),
}
}
fn consume_end_element(&mut self, expected_name: &str) {
match self.next().unwrap() {
ParseEvent::End(ref name) if expected_name == name => (),
event => panic!("Expected </{}>, found: {:?}", expected_name, event),
}
}
fn skip_to_end(&mut self, expected_name: &str) {
loop {
match self.next().unwrap() {
ParseEvent::End(ref name) if expected_name == name => break,
_ => {},
}
}
}
fn consume_two<'a, T: FromXml, U: FromXml>(
&mut self,
one: &'a str,
two: &'a str,
end: &'a str,
) -> (Vec<T>, Vec<U>) {
debug!("consume_two: looking for {} and {} until {}", one, two, end);
let mut ones = Vec::new();
let mut twos = Vec::new();
loop {
match self.next().unwrap() {
ParseEvent::Start(ref name, ref attributes) => {
debug!("Found start element <{:?} {:?}>", name, attributes);
debug!("one and two are {} and {}", one, two);
let n = name.clone();
if one == n {
ones.push(FromXml::convert(self, &attributes));
} else if "type" == n {
// XXX: GL1.1 contains types, which we never care about anyway.
// Make sure consume_two doesn't get used for things which *do*
// care about type.
warn!("Ignoring type!");
continue;
} else if two == n {
twos.push(FromXml::convert(self, &attributes));
} else {
panic!("Unexpected element: <{:?} {:?}>", n, &attributes);
}
},
ParseEvent::End(ref name) => {
debug!("Found end element </{:?}>", name);
if one == name || two == name {
continue;
} else if "type" == name {
// XXX: GL1.1 contains types, which we never care about anyway.
// Make sure consume_two doesn't get used for things which *do*
// care about type.
warn!("Ignoring type!");
continue;
} else if end == name {
return (ones, twos);
} else {
panic!("Unexpected end element {:?}", name);
}
},
event => panic!("Unexpected message {:?}", event),
}
}
}
fn consume_enums(&mut self, api: Api) -> Vec<Enum> {
let mut enums = Vec::new();
loop {
match self.next().unwrap() {
// ignores
ParseEvent::Text(_) => {},
ParseEvent::Start(ref name, _) if name == "unused" => self.skip_to_end("unused"),
// add enum definition
ParseEvent::Start(ref name, ref attributes) if name == "enum" => {
enums.push(self.consume_enum(api, attributes));
},
// finished building the namespace
ParseEvent::End(ref name) if name == "enums" => break,
// error handling
event => panic!("Expected </enums>, found: {:?}", event),
}
}
enums
}
fn consume_enum(&mut self, api: Api, attributes: &[Attribute]) -> Enum {
let ident = trim_enum_prefix(&get_attribute(&attributes, "name").unwrap(), api).to_string();
let value = get_attribute(&attributes, "value").unwrap();
let alias = get_attribute(&attributes, "alias");
let ty = get_attribute(&attributes, "type");
self.consume_end_element("enum");
match api {
Api::Egl => make_egl_enum(ident, ty, value, alias),
_ => make_enum(ident, ty, value, alias),
}
}
fn consume_groups(&mut self, api: Api) -> BTreeMap<String, Group> {
let mut groups = BTreeMap::new();
loop {
match self.next().unwrap() {
ParseEvent::Start(ref name, ref attributes) if name == "group" => {
let ident = get_attribute(&attributes, "name").unwrap();
let group = Group {
ident: ident.clone(),
enums_type: None,
enums: self.consume_group_enums(api)
};
groups.insert(ident, group);
},
ParseEvent::End(ref name) if name == "groups" => break,
event => panic!("Expected </groups>, found: {:?}", event),
}
}
groups
}
fn consume_group_enums(&mut self, api: Api) -> Vec<String> {
let mut enums = Vec::new();
loop {
match self.next().unwrap() {
ParseEvent::Start(ref name, ref attributes) if name == "enum" => {
let enum_name = get_attribute(&attributes, "name");
enums.push(trim_enum_prefix(&enum_name.unwrap(), api));
self.consume_end_element("enum");
},
ParseEvent::End(ref name) if name == "group" => break,
event => panic!("Expected </group>, found: {:?}", event),
}
}
enums
}
fn consume_cmds(&mut self, api: Api) -> (Vec<Cmd>, BTreeMap<String, Vec<String>>) {
let mut cmds = Vec::new();
let mut aliases: BTreeMap<String, Vec<String>> = BTreeMap::new();
loop {
match self.next().unwrap() {
// add command definition
ParseEvent::Start(ref name, _) if name == "command" => {
let new = self.consume_cmd(api);
if let Some(ref v) = new.alias {
match aliases.entry(v.clone()) {
Entry::Occupied(mut ent) => {
ent.get_mut().push(new.proto.ident.clone());
},
Entry::Vacant(ent) => {
ent.insert(vec![new.proto.ident.clone()]);
},
}
}
cmds.push(new);
},
// finished building the namespace
ParseEvent::End(ref name) if name == "commands" => break,
// error handling
event => panic!("Expected </commands>, found: {:?}", event),
}
}
(cmds, aliases)
}
fn consume_cmd(&mut self, api: Api) -> Cmd {
// consume command prototype
self.consume_start_element("proto");
let mut proto = self.consume_binding("proto", &[]);
proto.ident = trim_cmd_prefix(&proto.ident, api).to_string();
let mut params = Vec::new();
let mut alias = None;
let mut vecequiv = None;
let mut glx = None;
loop {
match self.next().unwrap() {
ParseEvent::Start(ref name, ref attributes) if name == "param" => {
params.push(self.consume_binding("param", attributes));
},
ParseEvent::Start(ref name, ref attributes) if name == "alias" => {
alias = get_attribute(&attributes, "name");
alias = alias.map(|t| trim_cmd_prefix(&t, api).to_string());
self.consume_end_element("alias");
},
ParseEvent::Start(ref name, ref attributes) if name == "vecequiv" => {
vecequiv = get_attribute(&attributes, "vecequiv");
self.consume_end_element("vecequiv");
},
ParseEvent::Start(ref name, ref attributes) if name == "glx" => {
glx = Some(GlxOpcode {
opcode: get_attribute(&attributes, "opcode").unwrap(),
name: get_attribute(&attributes, "name"),
});
self.consume_end_element("glx");
},
ParseEvent::End(ref name) if name == "command" => break,
event => panic!("Expected </command>, found: {:?}", event),
}
}
Cmd {
proto: proto,
params: params,
alias: alias,
vecequiv: vecequiv,
glx: glx,
}
}
fn consume_binding(&mut self, outside_tag: &str, attributes: &[Attribute]) -> Binding {
// consume type
let mut ty = String::new();
loop {
match self.next().unwrap() {
ParseEvent::Text(text) => ty.push_str(&text),
ParseEvent::Start(ref name, _) if name == "ptype" => (),
ParseEvent::End(ref name) if name == "ptype" => (),
ParseEvent::Start(ref name, _) if name == "name" => break,
event => panic!("Expected binding, found: {:?}", event),
}
}
// consume identifier
let ident = underscore_keyword(self.consume_characters());
self.consume_end_element("name");
// consume the type suffix
loop {
match self.next().unwrap() {
ParseEvent::Text(text) => ty.push_str(&text),
ParseEvent::End(ref name) if name == outside_tag => break,
event => panic!("Expected binding, found: {:?}", event),
}
}
Binding {
ident: ident,
ty: to_rust_ty(ty),
group: get_attribute(&attributes, "group"),
}
}
}
impl<T> Parse for T where T: Sized + Iterator<Item = ParseEvent> {}
fn get_attribute(attribs: &[Attribute], key: &str) -> Option<String> {
attribs
.iter()
.find(|attrib| attrib.key == key)
.map(|attrib| attrib.value.clone())
}
trait FromXml {
fn convert<P: Parse>(parser: &mut P, a: &[Attribute]) -> Self;
}
impl FromXml for Require {
fn convert<P: Parse>(parser: &mut P, _: &[Attribute]) -> Require {
debug!("Doing a FromXml on Require");
let (enums, commands) = parser.consume_two("enum", "command", "require");
Require {
enums: enums,
commands: commands,
}
}
}
impl FromXml for Remove {
fn convert<P: Parse>(parser: &mut P, a: &[Attribute]) -> Remove {
debug!("Doing a FromXml on Remove");
let profile = get_attribute(a, "profile").unwrap();
let profile = profile_from_str(&profile).unwrap();
let (enums, commands) = parser.consume_two("enum", "command", "remove");
Remove {
profile: profile,
enums: enums,
commands: commands,
}
}
}
impl FromXml for Feature {
fn convert<P: Parse>(parser: &mut P, a: &[Attribute]) -> Feature {
debug!("Doing a FromXml on Feature");
let api = get_attribute(a, "api").unwrap();
let api = api_from_str(&api).unwrap().unwrap();
let name = get_attribute(a, "name").unwrap();
let number = get_attribute(a, "number").unwrap();
debug!("Found api = {}, name = {}, number = {}", api, name, number);
let (require, remove) = parser.consume_two("require", "remove", "feature");
Feature {
api: api,
name: name,
number: number,
requires: require,
removes: remove,
}
}
}
impl FromXml for Extension {
fn convert<P: Parse>(parser: &mut P, a: &[Attribute]) -> Extension {
debug!("Doing a FromXml on Extension");
let name = get_attribute(a, "name").unwrap();
let supported = get_attribute(a, "supported")
.unwrap()
.split('|')
.filter_map(|api| {
api_from_str(api).unwrap_or_else(|()| panic!("unsupported API `{}`", api))
})
.collect::<Vec<_>>();
let mut require = Vec::new();
loop {
match parser.next().unwrap() {
ParseEvent::Start(ref name, ref attributes) if name == "require" => {
require.push(FromXml::convert(parser, &attributes));
},
ParseEvent::End(ref name) if name == "extension" => break,
event => panic!("Unexpected message {:?}", event),
}
}
Extension {
name: name,
supported: supported,
requires: require,
}
}
}
impl FromXml for String {
fn convert<P: Parse>(_: &mut P, a: &[Attribute]) -> String {
get_attribute(a, "name").unwrap()
}
}
/// Converts a C style type definition to the Rust equivalent
pub fn to_rust_ty<T: AsRef<str>>(ty: T) -> Cow<'static, str> {
let ty = match ty.as_ref().trim() {
// gl.xml types
"GLDEBUGPROC" => "types::GLDEBUGPROC",
"GLDEBUGPROCAMD" => "types::GLDEBUGPROCAMD",
"GLDEBUGPROCARB" => "types::GLDEBUGPROCARB",
"GLDEBUGPROCKHR" => "types::GLDEBUGPROCKHR",
"GLbitfield" => "types::GLbitfield",
"GLboolean" => "types::GLboolean",
"GLbyte" => "types::GLbyte",
"GLclampd" => "types::GLclampd",
"GLclampf" => "types::GLclampf",
"GLclampx" => "types::GLclampx",
"GLdouble" => "types::GLdouble",
"GLeglImageOES" => "types::GLeglImageOES",
"GLenum" => "types::GLenum",
"GLfixed" => "types::GLfixed",
"GLfloat" => "types::GLfloat",
"GLhalfNV" => "types::GLhalfNV",
"GLhandleARB" => "types::GLhandleARB",
"GLint" => "types::GLint",
"GLint64" => "types::GLint64",
"GLint64EXT" => "types::GLint64EXT",
"GLintptr" => "types::GLintptr",
"GLintptrARB" => "types::GLintptrARB",
"GLshort" => "types::GLshort",
"GLsizei" => "types::GLsizei",
"GLsizeiptr" => "types::GLsizeiptr",
"GLsizeiptrARB" => "types::GLsizeiptrARB",
"GLsync" => "types::GLsync",
"GLubyte" => "types::GLubyte",
"GLuint" => "types::GLuint",
"GLuint64" => "types::GLuint64",
"GLuint64EXT" => "types::GLuint64EXT",
"GLushort" => "types::GLushort",
"GLvdpauSurfaceNV" => "types::GLvdpauSurfaceNV",
"void" => "()",
"GLboolean *" => "*mut types::GLboolean",
"GLchar *" => "*mut types::GLchar",
"const GLchar*" => "*const types::GLchar",
"GLcharARB *" => "*mut types::GLcharARB",
"GLdouble *" => "*mut types::GLdouble",
"GLenum *" => "*mut types::GLenum",
"GLfixed *" => "*mut types::GLfixed",
"GLfloat *" => "*mut types::GLfloat",
"GLhandleARB *" => "*mut types::GLhandleARB",
"GLint *" => "*mut types::GLint",
"GLint64 *" => "*mut types::GLint64",
"GLint64EXT *" => "*mut types::GLint64EXT",
"GLsizei *" => "*mut types::GLsizei",
"GLubyte *" => "*mut types::GLubyte",
"GLuint *" => "*mut types::GLuint",
"GLuint [2]" => "*mut [types::GLuint; 2]",
"GLuint64 *" => "*mut types::GLuint64",
"GLuint64EXT *" => "*mut types::GLuint64EXT",
"GLushort *" => "*mut types::GLushort",
"GLvoid *" => "*mut types::GLvoid",
"GLvoid **" => "*const *mut types::GLvoid",
"void *" => "*mut __gl_imports::raw::c_void",
"void **" => "*const *mut __gl_imports::raw::c_void",
"const GLboolean *" => "*const types::GLboolean",
"const GLbyte *" => "*const types::GLbyte",
"const GLchar *" => "*const types::GLchar",
"const GLcharARB *" => "*const types::GLcharARB",
"const GLclampf *" => "*const types::GLclampf",
"const GLdouble *" => "*const types::GLdouble",
"const GLenum *" => "*const types::GLenum",
"const GLfixed *" => "*const types::GLfixed",
"const GLfloat" => "types::GLfloat",
"const GLfloat *" => "*const types::GLfloat",
"const GLhalfNV *" => "*const types::GLhalfNV",
"const GLint *" => "*const types::GLint",
"const GLint*" => "*const types::GLint",
"const GLint64 *" => "*const types::GLint64",
"const GLint64EXT *" => "*const types::GLint64EXT",
"const GLintptr *" => "*const types::GLintptr",
"const GLshort *" => "*const types::GLshort",
"const GLsizei*" |
"const GLsizei *" => "*const types::GLsizei",
"const GLsizeiptr *" => "*const types::GLsizeiptr",
"const GLubyte *" => "*const types::GLubyte",
"const GLuint *" => "*const types::GLuint",
"const GLuint64 *" => "*const types::GLuint64",
"const GLuint64EXT *" => "*const types::GLuint64EXT",
"const GLushort *" => "*const types::GLushort",
"const GLvdpauSurfaceNV *" => "*const types::GLvdpauSurfaceNV",
"const GLvoid *" => "*const types::GLvoid",
"const void*" |
"const void *" => "*const __gl_imports::raw::c_void",
"const void **" => "*const *const __gl_imports::raw::c_void",
"const void *const*" => "*const *const __gl_imports::raw::c_void",
"const GLboolean **" => "*const *const types::GLboolean",
"const GLchar **" => "*const *const types::GLchar",
"const GLcharARB **" => "*const *const types::GLcharARB",
"const GLvoid **" => "*const *const types::GLvoid",
"const GLchar *const*" => "*const *const types::GLchar",
"const GLvoid *const*" => "*const *const types::GLvoid",
"struct _cl_context *" => "*const types::_cl_context",
"struct _cl_event *" => "*const types::_cl_event",
"GLuint[2]" => "[Gluint; 2]",
// glx.xml types
"Bool" => "types::Bool",
"Colormap" => "types::Colormap",
"DMbuffer" => "types::DMbuffer",
"Font" => "types::Font",
"GLXContext" => "types::GLXContext",
"GLXContextID" => "types::GLXContextID",
"GLXDrawable" => "types::GLXDrawable",
"GLXFBConfig" => "types::GLXFBConfig",
"GLXFBConfigSGIX" => "types::GLXFBConfigSGIX",
"GLXPbuffer" => "types::GLXPbuffer",
"GLXPbufferSGIX" => "types::GLXPbufferSGIX",
"GLXPixmap" => "types::GLXPixmap",
"GLXVideoCaptureDeviceNV" => "types::GLXVideoCaptureDeviceNV",
"GLXVideoDeviceNV" => "types::GLXVideoDeviceNV",
"GLXVideoSourceSGIX" => "types::GLXVideoSourceSGIX",
"GLXWindow" => "types::GLXWindow",
// "GLboolean" => "types::GLboolean",
// "GLenum" => "types::GLenum",
// "GLint" => "types::GLint",
// "GLsizei" => "types::GLsizei",
// "GLuint" => "types::GLuint",
"Pixmap" => "types::Pixmap",
"Status" => "types::Status",
"VLNode" => "types::VLNode",
"VLPath" => "types::VLPath",
"VLServer" => "types::VLServer",
"Window" => "types::Window",
"__GLXextFuncPtr" => "types::__GLXextFuncPtr",
"const GLXContext" => "const types::GLXContext",
"float" => "__gl_imports::raw::c_float",
"int" => "__gl_imports::raw::c_int",
"int64_t" => "i64",
"unsigned int" => "__gl_imports::raw::c_uint",
"unsigned long" => "__gl_imports::raw::c_ulong",
// "void " => "()",
"DMparams *" => "*mut types::DMparams",
"Display *" => "*mut types::Display",
"GLXFBConfig *" => "*mut types::GLXFBConfig",
"GLXFBConfigSGIX *" => "*mut types::GLXFBConfigSGIX",
"GLXHyperpipeConfigSGIX *" => "*mut types::GLXHyperpipeConfigSGIX",
"GLXHyperpipeNetworkSGIX *" => "*mut types::GLXHyperpipeNetworkSGIX",
"GLXVideoCaptureDeviceNV *" => "*mut types::GLXVideoCaptureDeviceNV",
"GLXVideoDeviceNV *" => "*mut types::GLXVideoDeviceNV",
// "GLuint *" => "*mut types::GLuint",
"XVisualInfo *" => "*mut types::XVisualInfo",
// "const GLubyte *" => "*GLubyte",
"const char *" => "*const __gl_imports::raw::c_char",
"const int *" => "*const __gl_imports::raw::c_int",
// "const void *" => "*const __gl_imports::raw::c_void",
"int *" => "*mut __gl_imports::raw::c_int",
"int32_t *" => "*mut i32",
"int64_t *" => "*mut i64",
"long *" => "*mut __gl_imports::raw::c_long",
"unsigned int *" => "*mut __gl_imports::raw::c_uint",
"unsigned long *" => "*mut __gl_imports::raw::c_ulong",
// "void *" => "*mut __gl_imports::raw::c_void",
// wgl.xml types
"BOOL" => "types::BOOL",
"DWORD" => "types::DWORD",
"FLOAT" => "types::FLOAT",
// "GLbitfield" => "types::GLbitfield",
// "GLboolean" => "types::GLboolean",
// "GLenum" => "types::GLenum",
// "GLfloat" => "types::GLfloat",
// "GLint" => "types::GLint",
// "GLsizei" => "types::GLsizei",
// "GLuint" => "types::GLuint",
// "GLushort" => "types::GLushort",
"HANDLE" => "types::HANDLE",
"HDC" => "types::HDC",
"HENHMETAFILE" => "types::HENHMETAFILE",
"HGLRC" => "types::HGLRC",
"HGPUNV" => "types::HGPUNV",
"HPBUFFERARB" => "types::HPBUFFERARB",
"HPBUFFEREXT" => "types::HPBUFFEREXT",
"HPVIDEODEV" => "types::HPVIDEODEV",
"HVIDEOINPUTDEVICENV" => "types::HVIDEOINPUTDEVICENV",
"HVIDEOOUTPUTDEVICENV" => "types::HVIDEOOUTPUTDEVICENV",
"INT" => "types::INT",
"INT64" => "types::INT64",
"LPCSTR" => "types::LPCSTR",
"LPGLYPHMETRICSFLOAT" => "types::LPGLYPHMETRICSFLOAT",
"LPVOID" => "types::LPVOID",
"PGPU_DEVICE" => "types::PGPU_DEVICE",
"PROC" => "types::PROC",
"UINT" => "types::UINT",
"VOID" => "types::VOID",
// "int " => "__gl_imports::raw::c_int",
// "unsigned int " => "__gl_imports::raw::c_uint",
// "void " => "()",
"BOOL *" => "*mut types::BOOL",
"DWORD *" => "*mut types::DWORD",
"FLOAT *" => "*mut types::FLOAT",
// "GLuint *" => "*mut types::GLuint",
"HANDLE *" => "*mut types::HANDLE",
"HGPUNV *" => "*mut types::HGPUNV",
"HPVIDEODEV *" => "*mut types::HPVIDEODEV",
"HVIDEOINPUTDEVICENV *" => "*mut types::HVIDEOINPUTDEVICENV",
"HVIDEOOUTPUTDEVICENV *" => "*mut types::HVIDEOOUTPUTDEVICENV",
"INT32 *" => "*mut types::INT32",
"INT64 *" => "*mut types::INT64",
"UINT *" => "*mut types::UINT",
"USHORT *" => "*mut types::USHORT",
"const COLORREF *" => "*const types::COLORREF",
"const DWORD *" => "*const types::DWORD",
"const FLOAT *" => "*const types::FLOAT",
// "const GLushort *" => "*const types::GLushort",
"const HANDLE *" => "*const types::HANDLE",
"const HGPUNV *" => "*const types::HGPUNV",
"const LAYERPLANEDESCRIPTOR *" => "*const types::LAYERPLANEDESCRIPTOR",
"const LPVOID *" => "*const types::LPVOID",
"const PIXELFORMATDESCRIPTOR *" => "*const types::IXELFORMATDESCRIPTOR",
"const USHORT *" => "*const types::USHORT",
// "const char *" => "*const __gl_imports::raw::c_char",
// "const int *" => "*const __gl_imports::raw::c_int",
"float *" => "*mut __gl_imports::raw::c_float",
// "int *" => "*mut __gl_imports::raw::c_int",
// "unsigned long *" => "*mut __gl_imports::raw::c_ulong",
// "void *" => "*mut __gl_imports::raw::c_void",
// elx.xml types
"khronos_utime_nanoseconds_t" => "types::khronos_utime_nanoseconds_t",
"khronos_uint64_t" => "types::khronos_uint64_t",
"khronos_ssize_t" => "types::khronos_ssize_t",
"EGLNativeDisplayType" => "types::EGLNativeDisplayType",
"EGLNativePixmapType" => "types::EGLNativePixmapType",
"EGLNativeWindowType" => "types::EGLNativeWindowType",
"EGLint" => "types::EGLint",
"EGLint *" => "*mut types::EGLint",
"const EGLint *" => "*const types::EGLint",
"NativeDisplayType" => "types::NativeDisplayType",
"NativePixmapType" => "types::NativePixmapType",
"NativeWindowType" => "types::NativeWindowType",
//"Bool" => "types::Bool",
"EGLBoolean" => "types::EGLBoolean",
"EGLenum" => "types::EGLenum",
"EGLAttribKHR" => "types::EGLAttribKHR",
"EGLAttrib" => "types::EGLAttrib",
"EGLAttrib *" => "*mut types::EGLAttrib",
"const EGLAttrib *" => "*const types::EGLAttrib",
"const EGLattrib *" => "*const types::EGLAttrib", // Due to a typo in khronos_api/api_angle/scripts/egl_angle_ext.xml - see brendanzab/gl-rs#491
"EGLConfig" => "types::EGLConfig",
"EGLConfig *" => "*mut types::EGLConfig",
"EGLContext" => "types::EGLContext",
"EGLDeviceEXT" => "types::EGLDeviceEXT",
"EGLDisplay" => "types::EGLDisplay",
"EGLSurface" => "types::EGLSurface",
"EGLClientBuffer" => "types::EGLClientBuffer",
"__eglMustCastToProperFunctionPointerType" => {
"types::__eglMustCastToProperFunctionPointerType"
},
"EGLImageKHR" => "types::EGLImageKHR",
"EGLImage" => "types::EGLImage",
"EGLOutputLayerEXT" => "types::EGLOutputLayerEXT",
"EGLOutputPortEXT" => "types::EGLOutputPortEXT",
"EGLSyncKHR" => "types::EGLSyncKHR",
"EGLSync" => "types::EGLSync",
"EGLTimeKHR" => "types::EGLTimeKHR",
"EGLTime" => "types::EGLTime",
"EGLSyncNV" => "types::EGLSyncNV",
"EGLTimeNV" => "types::EGLTimeNV",
"EGLuint64NV" => "types::EGLuint64NV",
"EGLStreamKHR" => "types::EGLStreamKHR",
"EGLuint64KHR" => "types::EGLuint64KHR",
"EGLNativeFileDescriptorKHR" => "types::EGLNativeFileDescriptorKHR",
"EGLsizeiANDROID" => "types::EGLsizeiANDROID",
"EGLSetBlobFuncANDROID" => "types::EGLSetBlobFuncANDROID",
"EGLGetBlobFuncANDROID" => "types::EGLGetBlobFuncANDROID",
"EGLClientPixmapHI" => "types::EGLClientPixmapHI",
"struct EGLClientPixmapHI *" => "*const types::EGLClientPixmapHI",
"const EGLAttribKHR *" => "*const types::EGLAttribKHR",
"const EGLuint64KHR *" => "*const types::EGLuint64KHR",
"EGLAttribKHR *" => "*mut types::EGLAttribKHR",
"EGLDeviceEXT *" => "*mut types::EGLDeviceEXT",
"EGLNativeDisplayType *" => "*mut types::EGLNativeDisplayType",
"EGLNativePixmapType *" => "*mut types::EGLNativePixmapType",
"EGLNativeWindowType *" => "*mut types::EGLNativeWindowType",
"EGLOutputLayerEXT *" => "*mut types::EGLOutputLayerEXT",
"EGLTimeKHR *" => "*mut types::EGLTimeKHR",
"EGLOutputPortEXT *" => "*mut types::EGLOutputPortEXT",
"EGLuint64KHR *" => "*mut types::EGLuint64KHR",
"const struct AHardwareBuffer *" => "*const __gl_imports::raw::c_void", // humm
"GLeglClientBufferEXT" => "types::GLeglClientBufferEXT",
"GLVULKANPROCNV" => "types::GLVULKANPROCNV",
"EGLDEBUGPROCKHR" => "types::EGLDEBUGPROCKHR",
"EGLObjectKHR" => "types::EGLObjectKHR",
"EGLLabelKHR" => "types::EGLLabelKHR",
"EGLnsecsANDROID" => "types::EGLnsecsANDROID",
"EGLnsecsANDROID *" => "*mut types::EGLnsecsANDROID",
"EGLBoolean *" => "*mut types::EGLBoolean",
// failure
_ => panic!("Type conversion not implemented for `{}`", ty.as_ref()),
};
Cow::Borrowed(ty)
}
#[cfg(test)]
mod tests {
mod underscore_numeric_prefix {
use registry::parse;
#[test]
fn test_numeric_prefix() {
assert_eq!(parse::underscore_numeric_prefix("3"), "_3");
assert_eq!(parse::underscore_numeric_prefix("123_FOO"), "_123_FOO");
}
#[test]
fn test_non_numeric_prefix() {
assert_eq!(parse::underscore_numeric_prefix(""), "");
assert_eq!(parse::underscore_numeric_prefix("A"), "A");
assert_eq!(parse::underscore_numeric_prefix("FOO"), "FOO");
}
}
mod underscore_keyword {
use registry::parse;
#[test]
fn test_keyword() {
assert_eq!(parse::underscore_keyword("in".to_string()), "in_");
assert_eq!(parse::underscore_keyword("ref".to_string()), "ref_");
assert_eq!(parse::underscore_keyword("type".to_string()), "type_");
}
#[test]
fn test_non_keyword() {
assert_eq!(parse::underscore_keyword("foo".to_string()), "foo");
assert_eq!(parse::underscore_keyword("bar".to_string()), "bar");
}
}
mod make_enum {
use registry::parse;
#[test]
fn test_cast_0() {
let e = parse::make_enum(
"FOO".to_string(),
None,
"((EGLint)-1)".to_string(),
Some("BAR".to_string()),
);
assert_eq!(e.ident, "FOO");
assert_eq!((&*e.ty, &*e.value), ("EGLint", "-1"));
assert_eq!(e.alias, Some("BAR".to_string()));
}
#[test]
fn test_cast_1() {
let e = parse::make_enum(
"FOO".to_string(),
None,
"((EGLint)(-1))".to_string(),
Some("BAR".to_string()),
);
assert_eq!(e.ident, "FOO");
assert_eq!((&*e.ty, &*e.value), ("EGLint", "(-1)"));
assert_eq!(e.alias, Some("BAR".to_string()));
}
#[test]
fn test_no_type() {
let e = parse::make_enum(
"FOO".to_string(),
None,
"value".to_string(),
Some("BAR".to_string()),
);
assert_eq!(e.ident, "FOO");
assert_eq!(e.value, "value");
assert_eq!(e.alias, Some("BAR".to_string()));
assert_eq!(e.ty, "GLenum");
assert_eq!(e.cast, false);
}
#[test]
fn test_u() {
let e = parse::make_enum(
"FOO".to_string(),
Some("u".to_string()),
String::new(),
None,
);
assert_eq!(e.ty, "GLuint");
}
#[test]
fn test_ull() {
let e = parse::make_enum(
"FOO".to_string(),
Some("ull".to_string()),
String::new(),
None,
);
assert_eq!(e.ty, "GLuint64");
}
#[test]
#[should_panic]
fn test_unknown_type() {
parse::make_enum(
"FOO".to_string(),
Some("blargh".to_string()),
String::new(),
None,
);
}
#[test]
fn test_value_str() {
let e = parse::make_enum("FOO".to_string(), None, "\"hi\"".to_string(), None);
assert_eq!(e.ty, "&'static str");
}
#[test]
fn test_ident_true() {
let e = parse::make_enum("TRUE".to_string(), None, String::new(), None);
assert_eq!(e.ty, "GLboolean");
}
#[test]
fn test_ident_false() {
let e = parse::make_enum("FALSE".to_string(), None, String::new(), None);
assert_eq!(e.ty, "GLboolean");
}
}
mod make_egl_enum {
use registry::parse;
#[test]
fn test_cast_egl() {
let e = parse::make_egl_enum(
"FOO".to_string(),
None,
"EGL_CAST(EGLint,-1)".to_string(),
Some("BAR".to_string()),
);
assert_eq!(e.ident, "FOO");
assert_eq!((&*e.ty, &*e.value), ("EGLint", "-1"));
assert_eq!(e.alias, Some("BAR".to_string()));
}
#[test]
fn test_ident_true() {
let e = parse::make_egl_enum("TRUE".to_string(), None, "1234".to_string(), None);
assert_eq!(e.ty, "EGLBoolean");
}
#[test]
fn test_ident_false() {
let e = parse::make_egl_enum("FALSE".to_string(), None, "1234".to_string(), None);
assert_eq!(e.ty, "EGLBoolean");
}
#[test]
fn test_ull() {
let e = parse::make_egl_enum(
"FOO".to_string(),
Some("ull".to_string()),
"1234".to_string(),
None,
);
assert_eq!(e.ty, "EGLuint64KHR");
}
#[test]
fn test_negative_value() {
let e = parse::make_egl_enum("FOO".to_string(), None, "-1".to_string(), None);
assert_eq!(e.ty, "EGLint");
}
#[test]
#[should_panic]
fn test_unknown_type() {
parse::make_egl_enum(
"FOO".to_string(),
Some("blargh".to_string()),
String::new(),
None,
);
}
#[test]
#[should_panic]
fn test_unknown_value() {
parse::make_egl_enum("FOO".to_string(), None, "a".to_string(), None);
}
#[test]
#[should_panic]
fn test_empty_value() {
parse::make_egl_enum("FOO".to_string(), None, String::new(), None);
}
}
mod parse_event {
mod from_xml {
use xml::attribute::OwnedAttribute;
use xml::common::XmlVersion;
use xml::name::OwnedName;
use xml::namespace::Namespace;
use xml::reader::XmlEvent;
use registry::parse::{Attribute, ParseEvent};
#[test]
fn test_start_event() {
let given = XmlEvent::StartElement {
name: OwnedName::local("element"),
attributes: vec![
OwnedAttribute::new(OwnedName::local("attr1"), "val1"),
OwnedAttribute::new(OwnedName::local("attr2"), "val2"),
],
namespace: Namespace::empty(),
};
let expected = ParseEvent::Start(
"element".to_string(),
vec![
Attribute::new("attr1", "val1"),
Attribute::new("attr2", "val2"),
],
);
assert_eq!(ParseEvent::from_xml(given), Some(expected));
}
#[test]
fn test_end_element() {
let given = XmlEvent::EndElement {
name: OwnedName::local("element"),
};
let expected = ParseEvent::End("element".to_string());
assert_eq!(ParseEvent::from_xml(given), Some(expected));
}
#[test]
fn test_characters() {
let given = XmlEvent::Characters("text".to_string());
let expected = ParseEvent::Text("text".to_string());
assert_eq!(ParseEvent::from_xml(given), Some(expected));
}
#[test]
fn test_start_document() {
let given = XmlEvent::StartDocument {
version: XmlVersion::Version10,
encoding: "".to_string(),
standalone: None,
};
assert_eq!(ParseEvent::from_xml(given), None);
}
#[test]
fn test_end_document() {
let given = XmlEvent::EndDocument;
assert_eq!(ParseEvent::from_xml(given), None);
}
#[test]
fn test_processing_instruction() {
let given = XmlEvent::ProcessingInstruction {
name: "".to_string(),
data: None,
};
assert_eq!(ParseEvent::from_xml(given), None);
}
#[test]
fn test_cdata() {
let given = XmlEvent::CData("CData".to_string());
assert_eq!(ParseEvent::from_xml(given), None);
}
#[test]
fn test_comment() {
let given = XmlEvent::Comment("Comment".to_string());
assert_eq!(ParseEvent::from_xml(given), None);
}
#[test]
fn test_whitespace() {
let given = XmlEvent::Whitespace("Whitespace".to_string());
assert_eq!(ParseEvent::from_xml(given), None);
}
}
}
}