//! Rust API for the [OpenGL API and Extension Registry]. //! //! # Usage //! ``` //! use opengl_registry::Registry; //! //! let registry = Registry::retrieve().unwrap(); //! //! for command in registry.commands() { //! println!("Command {}", command.prototype().name()); //! println!(" Return type: {}", command.prototype().return_type()); //! println!(" Parameters:"); //! //! for param in command.parameters() { //! println!(" {} {}", param.get_type(), param.name()); //! } //! } //! ``` //! //! [OpenGL API and Extension Registry]: https://github.com/KhronosGroup/OpenGL-Registry #![cfg_attr(doc_cfg, feature(doc_cfg))] #![deny(clippy::all, clippy::pedantic, missing_docs)] use std::convert::Infallible; use std::fs::File; use std::io::Read; use xml_stinks::deserializer::buffered::Buffered as BufferedDeserializer; use xml_stinks::deserializer::{Deserializer, Error as DeserializerError, IgnoreEnd}; use xml_stinks::tagged::TagStart; use xml_stinks::{ impl_from_deserializer_error, xml_stinks_if_deserializer_static_generics, DeserializeTagged, }; use crate::api_interface_definition::{ APIInterfaceDefinition, Error as APIInterfaceDefinitionError, }; use crate::command::{Command, Error as CommandError}; use crate::util::impl_from_deserializer_err_wrapped; pub mod api_interface_definition; pub mod command; mod util; #[cfg(test)] mod test_utils; /// XML. #[cfg(feature = "include-xml")] const GL_REGISTRY_XML: &[u8] = include_bytes!("../OpenGL-Registry/xml/gl.xml"); const REGISTRY_TAG_NAME: &str = "registry"; /// Representation of the OpenGL registry. #[derive(Debug, PartialEq, Eq)] pub struct Registry { commands: Vec, api_interface_definitions: Vec, } impl Registry { /// Retrieves the OpenGL registry from a included XML file. /// /// # Errors /// Returns `Err` if parsing fails in any way. #[cfg(feature = "include-xml")] #[cfg_attr(doc_cfg, doc(cfg(feature = "include-xml")))] pub fn retrieve() -> Result { Self::retrieve_from_bytes(GL_REGISTRY_XML) } /// Retrieves the OpenGL registry from XML bytes. /// /// # Errors /// Returns `Err` if parsing fails in any way. pub fn retrieve_from_bytes(xml_bytes: &[u8]) -> Result { let mut deserializer = xml_stinks_if_deserializer_static_generics!(then { // The deserializer-static-generics feature of the dependency xml-stinks is // enabled. // // The feature is enabled when building tests and is required for mocking to // work. However, it will reject any non-owned source, so the bytes has to be // made into a Cursor> BufferedDeserializer::new(std::io::Cursor::new(xml_bytes.to_vec())); } else { BufferedDeserializer::new(xml_bytes); }); deserializer.skip_to_tag_start(REGISTRY_TAG_NAME)?; let registry = deserializer.de_tag::(REGISTRY_TAG_NAME, IgnoreEnd::Yes)?; Ok(registry) } /// Retrieves the OpenGL registry from a XML file. /// /// # Errors /// Returns `Err` if: /// - Parsing fails in any way. /// - An I/O error occurs. pub fn retrieve_from_file(xml_file: &mut File) -> Result { let mut buf = Vec::new(); xml_file.read_to_end(&mut buf)?; Self::retrieve_from_bytes(&buf) } /// Creates a new `Registry`. /// /// # Note /// This will **NOT** use anything from the actual OpenGL registry. Use the /// [`Registry::retrieve`] method for that. pub fn new( commands: impl IntoIterator, api_interface_definitions: impl IntoIterator, ) -> Self { Self { commands: commands.into_iter().collect(), api_interface_definitions: api_interface_definitions.into_iter().collect(), } } /// Returns the commands. #[must_use] pub fn commands(&self) -> &[Command] { &self.commands } /// Returns the API interface definitions. #[must_use] pub fn api_interface_definitions(&self) -> &[APIInterfaceDefinition] { &self.api_interface_definitions } } impl DeserializeTagged for Registry { type Error = RegistryError; fn deserialize( _start: &TagStart, deserializer: &mut TDeserializer, ) -> Result { deserializer.skip_to_tag_start("commands")?; let commands = deserializer.de_tag_with("commands", IgnoreEnd::No, |_, deserializer| { deserializer.de_tag_list::(Some("command")) })?; deserializer.skip_to_tag_start("feature")?; let api_interface_definitions = deserializer.de_tag_list::(Some("feature"))?; Ok(Self { commands, api_interface_definitions, }) } } /// [`Registry`] error. #[derive(Debug, thiserror::Error)] pub enum RegistryError { /// No 'registry' element was found. #[error("No 'registry' element was found")] MissingRegistryElement, /// No 'commands' element was found. #[error("No 'commands' element was found")] MissingCommandsElement, /// Invalid command. #[error("Invalid command")] InvalidCommand(#[from] CommandError), /// Invalid API interface definition. #[error("Invalid API interface definition")] InvalidAPIInterfaceDefinition(#[from] APIInterfaceDefinitionError), /// I/O failed. #[error("I/O failed")] IOFailed(#[from] std::io::Error), /// Deserialization failed. #[error("Deserialization failed")] DeserializationFailed(#[from] DeserializationError), } impl_from_deserializer_err_wrapped!(RegistryError); impl_from_deserializer_error!(RegistryError); /// Deserialization error. #[derive(Debug, thiserror::Error)] #[error(transparent)] pub struct DeserializationError(#[from] DeserializerError); #[cfg(test)] mod tests { use super::*; #[test] fn registry_works() { let registry = Registry::retrieve().expect("Expected Ok"); for api_interface_def in registry.api_interface_definitions() { println!( "{} - {}", api_interface_def.api_name(), api_interface_def.version() ); println!("Removals:"); for removal in api_interface_def.removals() { for feature in removal.features() { println!(" {:?} - {}", feature.kind(), feature.name()); } } } } }