//! Rust API for the [OpenGL reference page sources]. //! //! [OpenGL reference page sources]: https://github.com/KhronosGroup/OpenGL-Refpages #![cfg_attr(doc_cfg, feature(doc_cfg))] #![deny(clippy::all, clippy::pedantic, missing_docs)] use std::os::unix::prelude::OsStrExt; use include_dir::{include_dir, Dir}; use crate::description::{Description, Error as DescriptionError}; use crate::xml::element::{Attribute, Elements, FromElements}; use crate::xml::parser::{Error as ParserError, Parser}; pub mod description; mod util; mod xml; static GL4_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/OpenGL-Refpages/gl4"); /// Reference entry. #[derive(Debug)] pub struct ReferenceEntry { purpose: String, description: Description, } impl ReferenceEntry { /// Returns a function reference entry. /// /// # Errors /// Returns `Err` if /// - No reference entry file was found. /// - Parsing the reference entry file data fails. /// - The reference entry file data is invalid. pub fn get_function(function_name: &str) -> Result { let function_file = GL4_DIR .files() .find_map(|file| { if file.path().extension()?.as_bytes() != b"xml" { return None; } if function_name.starts_with(file.path().file_stem()?.to_str()?) { Some(file) } else { None } }) .ok_or_else(|| ReferenceEntryError::NoFileFound(function_name.to_string()))?; let function_ref_content = function_file.contents(); let mut parser = Parser::new(function_ref_content); let root_elements = parser.parse()?; ReferenceEntry::from_elements(&root_elements) } /// Returns the reference entry purpose. #[must_use] pub fn purpose(&self) -> &str { &self.purpose } /// Returns the reference entry description. #[must_use] pub fn description(&self) -> &Description { &self.description } } impl FromElements for ReferenceEntry { type Error = ReferenceEntryError; fn from_elements(elements: &Elements) -> Result { let refentry_element = elements .get_first_tagged_with_name("refentry") .ok_or(ReferenceEntryError::MissingRefEntry)?; let refnamediv_element = refentry_element .child_elements() .get_first_tagged_with_name("refnamediv") .ok_or(ReferenceEntryError::MissingRefNameDiv)?; let refpurpose_element = refnamediv_element .child_elements() .get_first_tagged_with_name("refpurpose") .ok_or(ReferenceEntryError::MissingRefPurpose)?; let purpose = refpurpose_element .child_elements() .get_first_text_element() .cloned() .unwrap_or_default(); let description_refsect = refentry_element .child_elements() .get_first_tagged_with_name_and_attr( "refsect1", &Attribute { key: "xml:id".to_string(), value: b"description".to_vec(), }, ) .ok_or(ReferenceEntryError::MissingDescriptionRefSect)?; let description = Description::from_elements(description_refsect.child_elements())?; Ok(ReferenceEntry { purpose, description, }) } } /// [`ReferenceEntry`] error. #[derive(Debug, thiserror::Error)] pub enum ReferenceEntryError { /// No reference entry file was found. #[error("No reference entry file was found for '{0}'")] NoFileFound(String), /// No 'refentry' element was found. #[error("No 'refentry' element was found")] MissingRefEntry, /// No 'refnamediv' element was found. #[error("No 'refnamediv' element was found")] MissingRefNameDiv, /// No 'refpurpose' element was found. #[error("No 'refpurpose' element was found")] MissingRefPurpose, /// No 'refsect1' element was found with id 'description''. #[error("No 'refsect1' element was found with id 'description'")] MissingDescriptionRefSect, /// Invalid description. #[error("Invalid description")] InvalidDescription(#[from] DescriptionError), /// Parsing failed. #[error("Parsing failed")] ParsingFailed(#[from] ParserError), }