1use std::{
2 collections::HashSet,
3 io::{prelude::*, Result},
4 path::Path,
5};
6
7use super::{function, trait_impls};
8use crate::{
9 analysis::enums::Info,
10 codegen::{
11 general::{
12 self, allow_deprecated, cfg_condition, cfg_condition_no_doc, cfg_condition_string,
13 cfg_deprecated, derives, doc_alias, version_condition, version_condition_no_doc,
14 version_condition_string,
15 },
16 generate_default_impl,
17 },
18 config::gobjects::GObject,
19 env::Env,
20 file_saver,
21 library::*,
22 nameutil::{enum_member_name, use_glib_if_needed, use_glib_type},
23 traits::*,
24 version::Version,
25};
26
27pub fn generate(env: &Env, root_path: &Path, mod_rs: &mut Vec<String>) {
28 if !env
29 .analysis
30 .enumerations
31 .iter()
32 .any(|e| env.config.objects[&e.full_name].status.need_generate())
33 {
34 return;
35 }
36
37 let path = root_path.join("enums.rs");
38 file_saver::save_to_file(path, env.config.make_backup, |w| {
39 general::start_comments(w, &env.config)?;
40 general::uses(w, env, &env.analysis.enum_imports, None)?;
41 writeln!(w)?;
42
43 mod_rs.push("\nmod enums;".into());
44 for enum_analysis in &env.analysis.enumerations {
45 let config = &env.config.objects[&enum_analysis.full_name];
46 if !config.status.need_generate() {
47 continue;
48 }
49
50 let enum_ = enum_analysis.type_(&env.library);
51
52 if let Some(cfg) = version_condition_string(env, None, enum_.version, false, 0) {
53 mod_rs.push(cfg);
54 }
55 if let Some(cfg) = cfg_condition_string(config.cfg_condition.as_ref(), false, 0) {
56 mod_rs.push(cfg);
57 }
58 mod_rs.push(format!(
59 "{}{} use self::enums::{};",
60 enum_
61 .deprecated_version
62 .map(|_| "#[allow(deprecated)]\n")
63 .unwrap_or(""),
64 enum_analysis.visibility.export_visibility(),
65 enum_.name
66 ));
67
68 generate_enum(env, w, enum_, config, enum_analysis)?;
69 }
70
71 Ok(())
72 });
73}
74
75fn generate_enum(
76 env: &Env,
77 w: &mut dyn Write,
78 enum_: &Enumeration,
79 config: &GObject,
80 analysis: &Info,
81) -> Result<()> {
82 struct Member<'a> {
83 name: String,
84 c_name: String,
85 version: Option<Version>,
86 deprecated_version: Option<Version>,
87 cfg_condition: Option<&'a String>,
88 }
89
90 let mut members: Vec<Member<'_>> = Vec::new();
91 let mut vals: HashSet<String> = HashSet::new();
92 let sys_crate_name = env.sys_crate_import(analysis.type_id);
93
94 for member in &enum_.members {
95 let member_config = config.members.matched(&member.name);
96 if member.status.ignored() || vals.contains(&member.value) {
97 continue;
98 }
99 vals.insert(member.value.clone());
100 let deprecated_version = member_config
101 .iter()
102 .find_map(|m| m.deprecated_version)
103 .or(member.deprecated_version);
104 let version = member_config
105 .iter()
106 .find_map(|m| m.version)
107 .or(member.version);
108 let cfg_condition = member_config.iter().find_map(|m| m.cfg_condition.as_ref());
109 members.push(Member {
110 name: enum_member_name(&member.name),
111 c_name: member.c_identifier.clone(),
112 version,
113 deprecated_version,
114 cfg_condition,
115 });
116 }
117
118 cfg_deprecated(
119 w,
120 env,
121 Some(analysis.type_id),
122 enum_.deprecated_version,
123 false,
124 0,
125 )?;
126 version_condition(w, env, None, enum_.version, false, 0)?;
127 cfg_condition(w, config.cfg_condition.as_ref(), false, 0)?;
128 if config.must_use {
129 writeln!(w, "#[must_use]")?;
130 }
131
132 if let Some(ref d) = config.derives {
133 derives(w, d, 1)?;
134 } else {
135 writeln!(w, "#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]")?;
136 }
137 writeln!(w, "#[derive(Clone, Copy)]")?;
138 if config.exhaustive {
139 writeln!(w, "#[repr(i32)]")?;
140 } else {
141 writeln!(w, "#[non_exhaustive]")?;
142 }
143 doc_alias(w, &enum_.c_type, "", 0)?;
144
145 writeln!(w, "{} enum {} {{", analysis.visibility, enum_.name)?;
146 for member in &members {
147 cfg_deprecated(
148 w,
149 env,
150 Some(analysis.type_id),
151 member.deprecated_version,
152 false,
153 1,
154 )?;
155 version_condition(w, env, None, member.version, false, 1)?;
156 cfg_condition(w, member.cfg_condition.as_ref(), false, 1)?;
157 if member.c_name != member.name {
159 doc_alias(w, &member.c_name, "", 1)?;
160 }
161 if config.exhaustive {
162 writeln!(
163 w,
164 "\t{} = {}::{},",
165 member.name, sys_crate_name, member.c_name
166 )?;
167 } else {
168 writeln!(w, "\t{},", member.name)?;
169 }
170 }
171
172 if !config.exhaustive {
173 writeln!(
174 w,
175 "\
176 #[doc(hidden)]
177 __Unknown(i32),",
178 )?;
179 }
180
181 writeln!(w, "}}")?;
182
183 let any_deprecated_version = enum_
184 .deprecated_version
185 .or_else(|| members.iter().find_map(|m| m.deprecated_version));
186
187 let functions = analysis
188 .functions
189 .iter()
190 .filter(|f| f.status.need_generate())
191 .collect::<Vec<_>>();
192
193 if !functions.is_empty() {
194 writeln!(w)?;
195 version_condition(w, env, None, enum_.version, false, 0)?;
196 cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?;
197 allow_deprecated(w, enum_.deprecated_version, false, 0)?;
198 write!(w, "impl {} {{", analysis.name)?;
199 for func_analysis in functions {
200 function::generate(
201 w,
202 env,
203 Some(analysis.type_id),
204 func_analysis,
205 Some(&analysis.specials),
206 enum_.version,
207 false,
208 false,
209 1,
210 )?;
211 }
212 writeln!(w, "}}")?;
213 }
214
215 trait_impls::generate(
216 w,
217 env,
218 &analysis.name,
219 &analysis.functions,
220 &analysis.specials,
221 None,
222 None,
223 config.cfg_condition.as_deref(),
224 )?;
225
226 writeln!(w)?;
227
228 let maybe_inline = if members.len() <= 12 || config.exhaustive {
230 "#[inline]\n"
231 } else {
232 ""
233 };
234
235 version_condition(w, env, None, enum_.version, false, 0)?;
237 cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?;
238 allow_deprecated(w, any_deprecated_version, false, 0)?;
239 writeln!(
240 w,
241 "#[doc(hidden)]
242impl IntoGlib for {name} {{
243 type GlibType = {sys_crate_name}::{ffi_name};
244
245 {maybe_inline}fn into_glib(self) -> {sys_crate_name}::{ffi_name} {{",
246 sys_crate_name = sys_crate_name,
247 name = enum_.name,
248 ffi_name = enum_.c_type,
249 maybe_inline = maybe_inline
250 )?;
251
252 if config.exhaustive {
253 writeln!(
254 w,
255 "self as {sys_crate_name}::{ffi_name}",
256 sys_crate_name = sys_crate_name,
257 ffi_name = enum_.c_type,
258 )?;
259 } else {
260 writeln!(w, "match self {{",)?;
261 for member in &members {
262 version_condition_no_doc(w, env, None, member.version, false, 3)?;
263 cfg_condition_no_doc(w, member.cfg_condition.as_ref(), false, 3)?;
264 writeln!(
265 w,
266 "\t\t\tSelf::{} => {}::{},",
267 member.name, sys_crate_name, member.c_name
268 )?;
269 }
270 writeln!(w, "\t\t\tSelf::__Unknown(value) => value,")?;
271 writeln!(
272 w,
273 "\
274 }}"
275 )?;
276 }
277
278 writeln!(
279 w,
280 "\
281 }}
282}}
283"
284 )?;
285
286 let assert = if env.config.generate_safety_asserts {
287 "skip_assert_initialized!();\n\t\t"
288 } else {
289 ""
290 };
291
292 version_condition(w, env, None, enum_.version, false, 0)?;
294 cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?;
295 allow_deprecated(w, any_deprecated_version, false, 0)?;
296 writeln!(
297 w,
298 "#[doc(hidden)]
299impl FromGlib<{sys_crate_name}::{ffi_name}> for {name} {{
300 {maybe_inline}unsafe fn from_glib(value: {sys_crate_name}::{ffi_name}) -> Self {{
301 {assert}",
302 sys_crate_name = sys_crate_name,
303 name = enum_.name,
304 ffi_name = enum_.c_type,
305 assert = assert,
306 maybe_inline = maybe_inline
307 )?;
308 if config.exhaustive {
309 let all_members = members
310 .iter()
311 .map(|m| format!("{}::{}", sys_crate_name, m.c_name))
312 .collect::<Vec<_>>()
313 .join(", ");
314 writeln!(w, "debug_assert!([{all_members}].contains(&value));")?;
315 writeln!(w, "std::mem::transmute(value)",)?;
316 } else {
317 writeln!(w, "match value {{")?;
318 for member in &members {
319 version_condition_no_doc(w, env, None, member.version, false, 3)?;
320 cfg_condition_no_doc(w, member.cfg_condition.as_ref(), false, 3)?;
321 writeln!(
322 w,
323 "\t\t\t{}::{} => Self::{},",
324 sys_crate_name, member.c_name, member.name
325 )?;
326 }
327 writeln!(w, "\t\t\tvalue => Self::__Unknown(value),")?;
328 writeln!(
329 w,
330 "\
331 }}"
332 )?;
333 }
334
335 writeln!(
336 w,
337 "\
338 }}
339}}
340"
341 )?;
342
343 if let Some(ref domain) = enum_.error_domain {
345 let has_failed_member = members.iter().any(|m| m.name == "Failed");
346
347 version_condition(w, env, None, enum_.version, false, 0)?;
348 cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?;
349 allow_deprecated(w, any_deprecated_version, false, 0)?;
350 writeln!(
351 w,
352 "impl {glib_error_domain} for {name} {{
353 #[inline]
354 fn domain() -> {glib_quark} {{
355 {assert}",
356 name = enum_.name,
357 glib_error_domain = use_glib_type(env, "error::ErrorDomain"),
358 glib_quark = use_glib_type(env, "Quark"),
359 assert = assert
360 )?;
361
362 match domain {
363 ErrorDomain::Quark(quark) => {
364 writeln!(
365 w,
366 " static QUARK: ::std::sync::OnceLock<{0}ffi::GQuark> = ::std::sync::OnceLock::new();
367 let quark = *QUARK.get_or_init(|| unsafe {{
368 {0}ffi::g_quark_from_static_string(b\"{1}\\0\".as_ptr() as *const _)
369 }});
370 unsafe {{ from_glib(quark) }}",
371 use_glib_if_needed(env, ""),
372 quark,
373 )?;
374 }
375 ErrorDomain::Function(f) => {
376 writeln!(w, " unsafe {{ from_glib({sys_crate_name}::{f}()) }}")?;
377 }
378 }
379
380 writeln!(
381 w,
382 " }}
383
384 #[inline]
385 fn code(self) -> i32 {{
386 self.into_glib()
387 }}
388
389 #[inline]
390 #[allow(clippy::match_single_binding)]
391 fn from(code: i32) -> Option<Self> {{
392 {assert}match unsafe {{ from_glib(code) }} {{"
393 )?;
394
395 if has_failed_member && !config.exhaustive {
396 writeln!(w, "\t\t\tSelf::__Unknown(_) => Some(Self::Failed),")?;
397 }
398 writeln!(w, "\t\t\tvalue => Some(value),")?;
399
400 writeln!(
401 w,
402 "\
403 }}
404 }}
405}}
406"
407 )?;
408 }
409
410 if let Some(ref get_type) = enum_.glib_get_type {
412 let configured_functions = config.functions.matched("get_type");
413 let version = std::iter::once(enum_.version)
414 .chain(configured_functions.iter().map(|f| f.version))
415 .max()
416 .flatten();
417
418 version_condition(w, env, None, version, false, 0)?;
419 cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?;
420 allow_deprecated(w, enum_.deprecated_version, false, 0)?;
421 writeln!(
422 w,
423 "impl StaticType for {name} {{
424 #[inline]",
425 name = enum_.name,
426 )?;
427 doc_alias(w, get_type, "", 1)?;
428 writeln!(
429 w,
430 " fn static_type() -> {glib_type} {{
431 unsafe {{ from_glib({sys_crate_name}::{get_type}()) }}
432 }}
433 }}",
434 sys_crate_name = sys_crate_name,
435 get_type = get_type,
436 glib_type = use_glib_type(env, "Type")
437 )?;
438 writeln!(w)?;
439
440 version_condition(w, env, None, version, false, 0)?;
441 cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?;
442 allow_deprecated(w, enum_.deprecated_version, false, 0)?;
443 writeln!(
444 w,
445 "impl {has_param_spec} for {name} {{
446 type ParamSpec = {param_spec_enum};
447 type SetValue = Self;
448 type BuilderFn = fn(&str, Self) -> {param_spec_builder}<Self>;
449
450 fn param_spec_builder() -> Self::BuilderFn {{
451 Self::ParamSpec::builder_with_default
452 }}
453}}",
454 name = enum_.name,
455 has_param_spec = use_glib_type(env, "HasParamSpec"),
456 param_spec_enum = use_glib_type(env, "ParamSpecEnum"),
457 param_spec_builder = use_glib_type(env, "ParamSpecEnumBuilder"),
458 )?;
459 writeln!(w)?;
460
461 version_condition(w, env, None, version, false, 0)?;
462 cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?;
463 allow_deprecated(w, enum_.deprecated_version, false, 0)?;
464 writeln!(
465 w,
466 "impl {valuetype} for {name} {{
467 type Type = Self;
468}}",
469 name = enum_.name,
470 valuetype = use_glib_type(env, "value::ValueType"),
471 )?;
472 writeln!(w)?;
473
474 version_condition(w, env, None, version, false, 0)?;
475 cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?;
476 allow_deprecated(w, enum_.deprecated_version, false, 0)?;
477 writeln!(
478 w,
479 "unsafe impl<'a> {from_value_type}<'a> for {name} {{
480 type Checker = {genericwrongvaluetypechecker}<Self>;
481
482 #[inline]
483 unsafe fn from_value(value: &'a {gvalue}) -> Self {{
484 {assert}from_glib({glib}(value.to_glib_none().0))
485 }}
486}}",
487 name = enum_.name,
488 glib = use_glib_type(env, "gobject_ffi::g_value_get_enum"),
489 gvalue = use_glib_type(env, "Value"),
490 genericwrongvaluetypechecker = use_glib_type(env, "value::GenericValueTypeChecker"),
491 assert = assert,
492 from_value_type = use_glib_type(env, "value::FromValue"),
493 )?;
494 writeln!(w)?;
495
496 version_condition(w, env, None, version, false, 0)?;
497 cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?;
498 allow_deprecated(w, enum_.deprecated_version, false, 0)?;
499 writeln!(
500 w,
501 "impl ToValue for {name} {{
502 #[inline]
503 fn to_value(&self) -> {gvalue} {{
504 let mut value = {gvalue}::for_value_type::<Self>();
505 unsafe {{
506 {glib}(value.to_glib_none_mut().0, self.into_glib());
507 }}
508 value
509 }}
510
511 #[inline]
512 fn value_type(&self) -> {gtype} {{
513 Self::static_type()
514 }}
515}}",
516 name = enum_.name,
517 glib = use_glib_type(env, "gobject_ffi::g_value_set_enum"),
518 gvalue = use_glib_type(env, "Value"),
519 gtype = use_glib_type(env, "Type"),
520 )?;
521 writeln!(w)?;
522
523 version_condition(w, env, None, version, false, 0)?;
524 cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?;
525 allow_deprecated(w, enum_.deprecated_version, false, 0)?;
526 writeln!(
527 w,
528 "impl From<{name}> for {gvalue} {{
529 #[inline]
530 fn from(v: {name}) -> Self {{
531 {assert}ToValue::to_value(&v)
532 }}
533}}",
534 name = enum_.name,
535 gvalue = use_glib_type(env, "Value"),
536 assert = assert,
537 )?;
538 writeln!(w)?;
539 }
540
541 generate_default_impl(
542 w,
543 env,
544 config,
545 &enum_.name,
546 enum_.version,
547 enum_.members.iter(),
548 |member| {
549 let e_member = members.iter().find(|m| m.c_name == member.c_identifier)?;
550 let member_config = config.members.matched(&member.name);
551 let version = member_config
552 .iter()
553 .find_map(|m| m.version)
554 .or(e_member.version);
555 let cfg_condition = member_config.iter().find_map(|m| m.cfg_condition.as_ref());
556 Some((version, cfg_condition, e_member.name.as_str()))
557 },
558 )?;
559
560 Ok(())
561}