opengl_registry/
lib.rs

1//! Rust API for the [OpenGL API and Extension Registry].
2//!
3//! # Usage
4//! ```
5//! use opengl_registry::Registry;
6//!
7//! let registry = Registry::retrieve().unwrap();
8//!
9//! for command in registry.commands() {
10//!     println!("Command {}", command.prototype().name());
11//!     println!("  Return type: {}", command.prototype().return_type());
12//!     println!("  Parameters:");
13//!
14//!     for param in command.parameters() {
15//!         println!("  {} {}", param.get_type(), param.name());
16//!     }
17//! }
18//! ```
19//!
20//! [OpenGL API and Extension Registry]: https://github.com/KhronosGroup/OpenGL-Registry
21#![cfg_attr(doc_cfg, feature(doc_cfg))]
22#![deny(clippy::all, clippy::pedantic, missing_docs)]
23
24use std::fs::File;
25use std::io::Read;
26
27use crate::command::{Command, Error as CommandError};
28use crate::xml::element::{Element, Elements, FromElements};
29use crate::xml::parser::{Error as ParserError, Parser};
30
31pub mod command;
32
33mod xml;
34
35#[cfg(feature = "include-xml")]
36const GL_REGISTRY_XML: &str = include_str!("../OpenGL-Registry/xml/gl.xml");
37
38const REGISTRY_TAG_NAME: &str = "registry";
39
40/// Representation of the OpenGL registry.
41pub struct Registry
42{
43    commands: Vec<Command>,
44}
45
46impl Registry
47{
48    /// Retrieves the OpenGL registry from a included XML file.
49    ///
50    /// # Errors
51    /// Returns `Err` if parsing fails in any way.
52    #[cfg(feature = "include-xml")]
53    #[cfg_attr(doc_cfg, doc(cfg(feature = "include-xml")))]
54    pub fn retrieve() -> Result<Registry, RegistryError>
55    {
56        Self::retrieve_from_bytes(GL_REGISTRY_XML.as_bytes())
57    }
58
59    /// Retrieves the OpenGL registry from XML bytes.
60    ///
61    /// # Errors
62    /// Returns `Err` if parsing fails in any way.
63    pub fn retrieve_from_bytes(xml_bytes: &[u8]) -> Result<Registry, RegistryError>
64    {
65        let mut parser = Parser::new(xml_bytes);
66
67        let elements = parser.parse().map_err(ParsingError)?;
68
69        let registry_element = elements
70            .get_first_tagged_element(REGISTRY_TAG_NAME)
71            .ok_or(RegistryError::MissingRegistryElement)?;
72
73        let registry = Registry::from_elements(registry_element.child_elements())?;
74
75        Ok(registry)
76    }
77
78    /// Retrieves the OpenGL registry from a XML file.
79    ///
80    /// # Errors
81    /// Returns `Err` if:
82    /// - Parsing fails in any way.
83    /// - An I/O error occurs.
84    pub fn retrieve_from_file(xml_file: &mut File) -> Result<Registry, RegistryError>
85    {
86        let mut buf = Vec::new();
87
88        xml_file.read_to_end(&mut buf)?;
89
90        Self::retrieve_from_bytes(&buf)
91    }
92
93    /// Creates a new `Registry`.
94    ///
95    /// # Note
96    /// This will **NOT** use anything from the actual OpenGL registry. Use the
97    /// [`Registry::retrieve`] method for that.
98    pub fn new(commands: impl IntoIterator<Item = Command>) -> Self
99    {
100        Self {
101            commands: commands.into_iter().collect(),
102        }
103    }
104
105    /// Returns the available commands.
106    #[must_use]
107    pub fn commands(&self) -> &[Command]
108    {
109        &self.commands
110    }
111}
112
113impl FromElements for Registry
114{
115    type Error = RegistryError;
116
117    fn from_elements(elements: &Elements) -> Result<Self, Self::Error>
118    {
119        let commands_element = elements
120            .get_first_tagged_element("commands")
121            .ok_or(Self::Error::MissingCommandsElement)?;
122
123        let command_elements =
124            commands_element
125                .child_elements()
126                .into_iter()
127                .filter_map(|element| match element {
128                    Element::Tagged(tagged_element)
129                        if tagged_element.name() == "command" =>
130                    {
131                        Some(tagged_element)
132                    }
133                    _ => None,
134                });
135
136        let commands = command_elements
137            .into_iter()
138            .map(|command_element| {
139                Command::from_elements(command_element.child_elements())
140            })
141            .collect::<Result<Vec<_>, _>>()?;
142
143        Ok(Self { commands })
144    }
145}
146
147/// [`Registry`] error.
148#[derive(Debug, thiserror::Error)]
149pub enum RegistryError
150{
151    /// No 'registry' element was found.
152    #[error("No 'registry' element was found")]
153    MissingRegistryElement,
154
155    /// No 'commands' element was found.
156    #[error("No 'commands' element was found")]
157    MissingCommandsElement,
158
159    /// A command is invalid.
160    #[error("Invalid command")]
161    InvalidCommand(#[from] CommandError),
162
163    /// Parsing failed.
164    #[error("Parsing failed")]
165    ParsingFailed(#[from] ParsingError),
166
167    /// I/O failed.
168    #[error("I/O failed")]
169    IOFailed(#[from] std::io::Error),
170}
171
172/// Parsing error.
173#[derive(Debug, thiserror::Error)]
174#[error(transparent)]
175pub struct ParsingError(#[from] ParserError);