opengl_registry/
command.rs1use crate::xml::element::{Elements, FromElements};
3
4#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct Command
7{
8 prototype: Prototype,
9 parameters: Vec<Parameter>,
10}
11
12impl Command
13{
14 pub fn new(
16 prototype: Prototype,
17 parameters: impl IntoIterator<Item = Parameter>,
18 ) -> Self
19 {
20 Self {
21 prototype,
22 parameters: parameters.into_iter().collect(),
23 }
24 }
25
26 #[must_use]
28 pub fn prototype(&self) -> &Prototype
29 {
30 &self.prototype
31 }
32
33 #[must_use]
35 pub fn parameters(&self) -> &[Parameter]
36 {
37 &self.parameters
38 }
39}
40
41impl FromElements for Command
42{
43 type Error = Error;
44
45 fn from_elements(
46 elements: &crate::xml::element::Elements,
47 ) -> Result<Self, Self::Error>
48 {
49 let proto_element = elements
50 .get_first_tagged_element("proto")
51 .ok_or(Self::Error::MissingPrototype)?;
52
53 let prototype = Prototype::from_elements(proto_element.child_elements())?;
54
55 let parameters = elements
56 .get_all_tagged_elements_with_name("param")
57 .into_iter()
58 .map(|param_element| Parameter::from_elements(param_element.child_elements()))
59 .collect::<Result<Vec<_>, _>>()?;
60
61 Ok(Self {
62 prototype,
63 parameters,
64 })
65 }
66}
67
68#[derive(Debug, thiserror::Error)]
70pub enum Error
71{
72 #[error("No 'proto' element was found")]
74 MissingPrototype,
75
76 #[error("Invalid prototype")]
78 InvalidPrototype(#[from] PrototypeError),
79
80 #[error("Invalid parameter")]
82 InvalidParameter(#[from] ParameterError),
83}
84
85#[derive(Debug, Clone, PartialEq, Eq)]
87pub struct Prototype
88{
89 name: String,
90 return_type: String,
91}
92
93impl Prototype
94{
95 pub fn new(name: impl Into<String>, return_type: impl Into<String>) -> Self
97 {
98 Self {
99 name: name.into(),
100 return_type: return_type.into(),
101 }
102 }
103
104 #[must_use]
106 pub fn name(&self) -> &str
107 {
108 &self.name
109 }
110
111 #[must_use]
113 pub fn return_type(&self) -> &str
114 {
115 &self.return_type
116 }
117}
118
119impl FromElements for Prototype
120{
121 type Error = PrototypeError;
122
123 fn from_elements(
124 elements: &crate::xml::element::Elements,
125 ) -> Result<Self, Self::Error>
126 {
127 let name = elements
128 .get_first_tagged_element("name")
129 .ok_or(Self::Error::MissingName)?
130 .child_elements()
131 .get_first_text_element()
132 .cloned()
133 .unwrap_or_default();
134
135 let return_type = find_type(elements);
136
137 Ok(Self { name, return_type })
138 }
139}
140
141#[derive(Debug, thiserror::Error)]
143pub enum PrototypeError
144{
145 #[error("No 'name' element was found")]
147 MissingName,
148}
149
150#[derive(Debug, Clone, PartialEq, Eq)]
152pub struct Parameter
153{
154 name: String,
155 ty: String,
156}
157
158impl Parameter
159{
160 pub fn new(name: impl Into<String>, ty: impl Into<String>) -> Self
162 {
163 Self {
164 name: name.into(),
165 ty: ty.into(),
166 }
167 }
168
169 #[must_use]
171 pub fn name(&self) -> &str
172 {
173 &self.name
174 }
175
176 #[must_use]
178 pub fn get_type(&self) -> &str
179 {
180 &self.ty
181 }
182}
183
184impl FromElements for Parameter
185{
186 type Error = ParameterError;
187
188 fn from_elements(elements: &Elements) -> Result<Self, Self::Error>
189 {
190 let name = elements
191 .get_first_tagged_element("name")
192 .ok_or(Self::Error::MissingName)?
193 .child_elements()
194 .get_first_text_element()
195 .cloned()
196 .unwrap_or_default();
197
198 let ty = find_type(elements);
199
200 Ok(Self { name, ty })
201 }
202}
203
204#[derive(Debug, thiserror::Error)]
206pub enum ParameterError
207{
208 #[error("No 'name' element was found")]
210 MissingName,
211}
212
213fn find_type(elements: &Elements) -> String
214{
215 let text_type_parts = elements
216 .get_all_text_elements()
217 .into_iter()
218 .map(|text_type_part| text_type_part.trim())
219 .filter(|text_type_part| !text_type_part.is_empty())
220 .collect::<Vec<_>>();
221
222 let opt_ptype_text = get_ptype_text(elements);
223
224 opt_ptype_text.map_or_else(
225 || join_space_strs(text_type_parts.iter()),
226 |ptype_text| {
227 let Some(first_part) = text_type_parts.first() else {
228 return ptype_text.clone();
229 };
230
231 let before = if *first_part == "const" { "const " } else { "" };
232
233 let after_start_index = usize::from(*first_part == "const");
234
235 format!(
236 "{before}{ptype_text} {}",
237 text_type_parts
238 .get(after_start_index..)
239 .map(|parts| join_space_strs(parts.iter()))
240 .unwrap_or_default()
241 )
242 },
243 )
244}
245
246fn get_ptype_text(elements: &Elements) -> Option<&String>
247{
248 let ptype_element = elements.get_first_tagged_element("ptype")?;
249
250 ptype_element.child_elements().get_first_text_element()
251}
252
253fn join_space_strs<Strings, StrItem>(strings: Strings) -> String
254where
255 Strings: Iterator<Item = StrItem>,
256 StrItem: ToString,
257{
258 strings
259 .into_iter()
260 .map(|string| string.to_string())
261 .collect::<Vec<_>>()
262 .join(" ")
263}